Added Dohya
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
#if UNITY_5_6_OR_NEWER
|
||||
#define VLB_GPU_INSTANCING_SUPPORT
|
||||
#endif
|
||||
|
||||
// Force isDepthBlendEnabled at true when GPU Instancing is enabled, to prevent from breaking the batch if 1 beam has it at 0 and 1 has it at > 0
|
||||
#define FORCE_ENABLE_DEPTHBLEND_FOR_BATCHING
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
public static class BatchingHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns if GPU Instancing feature is supported on this Unity version or not.
|
||||
/// </summary>
|
||||
#if VLB_GPU_INSTANCING_SUPPORT
|
||||
public const bool isGpuInstancingSupported = true;
|
||||
#else
|
||||
public static bool isGpuInstancingSupported { get; private set; }
|
||||
#endif
|
||||
|
||||
#if FORCE_ENABLE_DEPTHBLEND_FOR_BATCHING
|
||||
public static bool forceEnableDepthBlend { get { return Config.Instance.actualRenderingMode == RenderingMode.GPUInstancing || Config.Instance.actualRenderingMode == RenderingMode.SRPBatcher; } }
|
||||
#else
|
||||
public const bool forceEnableDepthBlend = false;
|
||||
#endif
|
||||
|
||||
public static bool IsGpuInstancingEnabled(Material material)
|
||||
{
|
||||
#if VLB_GPU_INSTANCING_SUPPORT
|
||||
return material.enableInstancing;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void SetMaterialProperties(Material material, bool enableGpuInstancing)
|
||||
{
|
||||
#if VLB_GPU_INSTANCING_SUPPORT
|
||||
Debug.Assert(material != null);
|
||||
material.enableInstancing = enableGpuInstancing;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static bool CanBeBatched(VolumetricLightBeam beamA, VolumetricLightBeam beamB, ref string reasons)
|
||||
{
|
||||
#pragma warning disable 0162 // warning CS0162: Unreachable code detected
|
||||
if (Config.Instance.renderPipeline == RenderPipeline.BuiltIn && !isGpuInstancingSupported)
|
||||
{
|
||||
reasons = "'GPU Instancing' is not supported on your setup.";
|
||||
return false;
|
||||
}
|
||||
#pragma warning restore 0162
|
||||
|
||||
if (Config.Instance.actualRenderingMode != RenderingMode.GPUInstancing && Config.Instance.actualRenderingMode != RenderingMode.SRPBatcher)
|
||||
{
|
||||
reasons = string.Format("Current Render Pipeline is '{0}'. To enable batching, use 'GPU Instancing'", Config.Instance.renderPipeline);
|
||||
if(Config.Instance.renderPipeline != RenderPipeline.BuiltIn)
|
||||
reasons += " or 'SRP Batcher'";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = true;
|
||||
|
||||
if (!CanBeBatched(beamA, ref reasons))
|
||||
ret = false;
|
||||
|
||||
if (!CanBeBatched(beamB, ref reasons))
|
||||
ret = false;
|
||||
|
||||
if (Config.Instance.featureEnabledDynamicOcclusion)
|
||||
{
|
||||
if ((beamA.GetComponent<DynamicOcclusionAbstractBase>() == null) != (beamB.GetComponent<DynamicOcclusionAbstractBase>() == null))
|
||||
{
|
||||
AppendErrorMessage(ref reasons, string.Format("{0}/{1}: dynamically occluded and non occluded beams cannot be batched together", beamA.name, beamB.name));
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.Instance.featureEnabledColorGradient != FeatureEnabledColorGradient.Off && beamA.colorMode != beamB.colorMode)
|
||||
{
|
||||
AppendErrorMessage(ref reasons, string.Format("'Color Mode' mismatch: {0} / {1}", beamA.colorMode, beamB.colorMode));
|
||||
ret = false;
|
||||
}
|
||||
|
||||
if (beamA.blendingMode != beamB.blendingMode)
|
||||
{
|
||||
AppendErrorMessage(ref reasons, string.Format("'Blending Mode' mismatch: {0} / {1}", beamA.blendingMode, beamB.blendingMode));
|
||||
ret = false;
|
||||
}
|
||||
|
||||
if (Config.Instance.featureEnabledNoise3D && beamA.isNoiseEnabled != beamB.isNoiseEnabled)
|
||||
{
|
||||
AppendErrorMessage(ref reasons, string.Format("'3D Noise' enabled mismatch: {0} / {1}", beamA.noiseMode, beamB.noiseMode));
|
||||
ret = false;
|
||||
}
|
||||
|
||||
if (Config.Instance.featureEnabledDepthBlend && !forceEnableDepthBlend)
|
||||
{
|
||||
#pragma warning disable 0162
|
||||
if ((beamA.depthBlendDistance > 0) != (beamB.depthBlendDistance > 0))
|
||||
{
|
||||
AppendErrorMessage(ref reasons, string.Format("'Opaque Geometry Blending' mismatch: {0} / {1}", beamA.depthBlendDistance, beamB.depthBlendDistance));
|
||||
ret = false;
|
||||
}
|
||||
#pragma warning restore 0162
|
||||
}
|
||||
|
||||
if (Config.Instance.featureEnabledShaderAccuracyHigh && beamA.shaderAccuracy != beamB.shaderAccuracy)
|
||||
{
|
||||
AppendErrorMessage(ref reasons, string.Format("'Shader Accuracy' mismatch: {0} / {1}", beamA.shaderAccuracy, beamB.shaderAccuracy));
|
||||
ret = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static bool CanBeBatched(VolumetricLightBeam beam, ref string reasons)
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
if (Config.Instance.actualRenderingMode == RenderingMode.GPUInstancing)
|
||||
{
|
||||
if (beam.geomMeshType != MeshType.Shared)
|
||||
{
|
||||
AppendErrorMessage(ref reasons, string.Format("{0} is not using shared mesh", beam.name));
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.Instance.featureEnabledDynamicOcclusion && beam.GetComponent<DynamicOcclusionDepthBuffer>() != null)
|
||||
{
|
||||
AppendErrorMessage(ref reasons, string.Format("{0} is using the DynamicOcclusion DepthBuffer feature", beam.name));
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void AppendErrorMessage(ref string message, string toAppend)
|
||||
{
|
||||
if (message != "") message += "\n";
|
||||
message += "- " + toAppend;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb22530640d12f542baf4bb5bf6306ff
|
||||
timeCreated: 1538426592
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,613 @@
|
||||
#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.UrlBeam)]
|
||||
public class BeamGeometry : MonoBehaviour, MaterialModifier.Interface
|
||||
{
|
||||
VolumetricLightBeam m_Master = null;
|
||||
Matrix4x4 m_ColorGradientMatrix;
|
||||
MeshType m_CurrentMeshType = MeshType.Shared;
|
||||
Material m_CustomMaterial = null;
|
||||
MaterialModifier.Callback m_MaterialModifierCallback = null;
|
||||
Coroutine m_CoFadeOut = null;
|
||||
|
||||
public MeshRenderer meshRenderer { get; private set; }
|
||||
public MeshFilter meshFilter { get; private set; }
|
||||
public Mesh coneMesh { get; private set; }
|
||||
|
||||
public 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.FadeOutFactor, value);
|
||||
MaterialChangeStop();
|
||||
}
|
||||
else
|
||||
{
|
||||
meshRenderer.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void RestartFadeOutCoroutine()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isPlaying)
|
||||
#endif
|
||||
{
|
||||
if (m_CoFadeOut != null)
|
||||
{
|
||||
StopCoroutine(m_CoFadeOut);
|
||||
m_CoFadeOut = null;
|
||||
}
|
||||
|
||||
if (m_Master && m_Master.isFadeOutEnabled)
|
||||
{
|
||||
m_CoFadeOut = StartCoroutine(CoUpdateFadeOut());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
// Handle copy / paste the LightBeam in Editor
|
||||
if (!m_Master)
|
||||
DestroyImmediate(gameObject);
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (m_CustomMaterial)
|
||||
{
|
||||
DestroyImmediate(m_CustomMaterial);
|
||||
m_CustomMaterial = null;
|
||||
}
|
||||
}
|
||||
|
||||
#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.DynamicOcclusion.DepthTexture // sampler cannot be passed to shader as instanced property
|
||||
&& Config.Instance.actualRenderingMode == 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(VolumetricLightBeam 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 = MaterialManager.NewMaterialTransient(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()
|
||||
{
|
||||
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_Radius(1f, 1f, 1f, m_Master.geomCustomSides, m_Master.geomCustomSegments, m_Master.geomCap, Config.Instance.requiresDoubleSidedMesh);
|
||||
coneMesh.hideFlags = Consts.Internal.ProceduralObjectsHideFlags;
|
||||
meshFilter.mesh = coneMesh;
|
||||
break;
|
||||
}
|
||||
case MeshType.Shared:
|
||||
{
|
||||
coneMesh = GlobalMesh.Get();
|
||||
meshFilter.sharedMesh = coneMesh;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Debug.LogError("Unsupported MeshType");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateMaterialAndBounds();
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
bool ApplyMaterial()
|
||||
{
|
||||
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);
|
||||
|
||||
var staticProps = new MaterialManager.StaticProperties
|
||||
{
|
||||
blendingMode = (MaterialManager.BlendingMode)m_Master.blendingMode,
|
||||
noise3D = isNoiseEnabled ? MaterialManager.Noise3D.On : MaterialManager.Noise3D.Off,
|
||||
depthBlend = isDepthBlendEnabled ? MaterialManager.DepthBlend.On : MaterialManager.DepthBlend.Off,
|
||||
colorGradient = colorGradient,
|
||||
dynamicOcclusion = m_Master._INTERNAL_DynamicOcclusionMode_Runtime,
|
||||
meshSkewing = m_Master.hasMeshSkewing ? MaterialManager.MeshSkewing.On : MaterialManager.MeshSkewing.Off,
|
||||
shaderAccuracy = (m_Master.shaderAccuracy == ShaderAccuracy.Fast) ? MaterialManager.ShaderAccuracy.Fast : MaterialManager.ShaderAccuracy.High
|
||||
};
|
||||
|
||||
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.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.ConeApexOffsetZ, nonNullApex);
|
||||
|
||||
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.AlphaInside, intensityInside);
|
||||
SetMaterialProp(ShaderProperties.AlphaOutside, intensityOutside);
|
||||
SetMaterialProp(ShaderProperties.AttenuationLerpLinearQuad, m_Master.attenuationLerpLinearQuad);
|
||||
SetMaterialProp(ShaderProperties.DistanceFallOff, new Vector3(m_Master.fallOffStart, m_Master.fallOffEnd, m_Master.maxGeometryDistance));
|
||||
SetMaterialProp(ShaderProperties.DistanceCamClipping, m_Master.cameraClippingDistance);
|
||||
SetMaterialProp(ShaderProperties.FresnelPow, Mathf.Max(0.001f, m_Master.fresnelPow)); // no pow 0, otherwise will generate inf fresnel and issues on iOS
|
||||
SetMaterialProp(ShaderProperties.GlareBehind, m_Master.glareBehind);
|
||||
SetMaterialProp(ShaderProperties.GlareFrontal, m_Master.glareFrontal);
|
||||
SetMaterialProp(ShaderProperties.DrawCap, m_Master.geomCap ? 1 : 0);
|
||||
SetMaterialProp(ShaderProperties.TiltVector, m_Master.tiltFactor);
|
||||
SetMaterialProp(ShaderProperties.AdditionalClippingPlaneWS, m_Master.additionalClippingPlane);
|
||||
|
||||
if (isDepthBlendEnabled)
|
||||
{
|
||||
SetMaterialProp(ShaderProperties.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.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.actualRenderingMode == 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
|
||||
{
|
||||
Debug.Assert(cam.GetComponentInParent<VolumetricLightBeam>() == null);
|
||||
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.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 bool _EDITOR_IsUsingCustomMaterial { get { return m_CustomMaterial != null; } }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50a48a2b69575db4c8b5bf5aa8186d89
|
||||
timeCreated: 1504793414
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,40 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
struct CachedLightProperties
|
||||
{
|
||||
Light light;
|
||||
float range;
|
||||
float spotAngle;
|
||||
Color color;
|
||||
|
||||
public CachedLightProperties(Light lightParam)
|
||||
{
|
||||
light = lightParam;
|
||||
range = -1f;
|
||||
spotAngle = -1f;
|
||||
color = Color.white;
|
||||
|
||||
if (light && light.type == LightType.Spot)
|
||||
{
|
||||
range = light.range;
|
||||
spotAngle = light.spotAngle;
|
||||
color = light.color;
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode() { return light ? light.GetHashCode() : 0; }
|
||||
public bool Equals(CachedLightProperties other) { return Equals(other, this); }
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null || GetType() != obj.GetType())
|
||||
return false;
|
||||
|
||||
var other = (CachedLightProperties)obj;
|
||||
return other.range == range && other.spotAngle == spotAngle && other.color == color;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b455c84cf6c7ad74e84e15749caaccc3
|
||||
timeCreated: 1510650372
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,535 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
[HelpURL(Consts.Help.UrlConfig)]
|
||||
public class Config : ScriptableObject
|
||||
{
|
||||
public const string ClassName = "Config";
|
||||
|
||||
/// <summary>
|
||||
/// Override the layer on which the procedural geometry is created or not
|
||||
/// </summary>
|
||||
public bool geometryOverrideLayer = Consts.Config.GeometryOverrideLayerDefault;
|
||||
|
||||
/// <summary>
|
||||
/// The layer the procedural geometry gameObject is in (only if geometryOverrideLayer is enabled)
|
||||
/// </summary>
|
||||
public int geometryLayerID = Consts.Config.GeometryLayerIDDefault;
|
||||
|
||||
/// <summary>
|
||||
/// The tag applied on the procedural geometry gameObject
|
||||
/// </summary>
|
||||
public string geometryTag = Consts.Config.GeometryTagDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Determine in which order beams are rendered compared to other objects.
|
||||
/// This way for example transparent objects are rendered after opaque objects, and so on.
|
||||
/// </summary>
|
||||
public int geometryRenderQueue = (int)Consts.Config.GeometryRenderQueueDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Select the Render Pipeline (Built-In or SRP) in use.
|
||||
/// </summary>
|
||||
public RenderPipeline renderPipeline
|
||||
{
|
||||
get { return _RenderPipeline; }
|
||||
set
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
_RenderPipeline = value;
|
||||
#else
|
||||
Debug.LogError("Modifying the RenderPipeline in standalone builds is not permitted");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
[FormerlySerializedAs("renderPipeline")]
|
||||
[SerializeField] RenderPipeline _RenderPipeline = Consts.Config.GeometryRenderPipelineDefault;
|
||||
|
||||
/// <summary>
|
||||
/// MultiPass: Use the 2 pass shader. Will generate 2 drawcalls per beam.
|
||||
/// SinglePass: Use the 1 pass shader. Will generate 1 drawcall per beam.
|
||||
/// GPUInstancing: Dynamically batch multiple beams to combine and reduce draw calls (Feature only supported in Unity 5.6 or above). More info: https://docs.unity3d.com/Manual/GPUInstancing.html
|
||||
/// SRPBatcher: Use the SRP Batcher to automatically batch multiple beams and reduce draw calls. Only available when using SRP.
|
||||
/// </summary>
|
||||
public RenderingMode renderingMode
|
||||
{
|
||||
get { return _RenderingMode; }
|
||||
set
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
_RenderingMode = value;
|
||||
#else
|
||||
Debug.LogError("Modifying the RenderingMode in standalone builds is not permitted");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
[FormerlySerializedAs("renderingMode")]
|
||||
[SerializeField] RenderingMode _RenderingMode = Consts.Config.GeometryRenderingModeDefault;
|
||||
|
||||
|
||||
public bool IsSRPBatcherSupported()
|
||||
{
|
||||
// The SRP Batcher Rendering Mode is only compatible when using a SRP
|
||||
if (renderPipeline == RenderPipeline.BuiltIn) return false;
|
||||
|
||||
// SRP Batcher only works with URP and HDRP
|
||||
var rp = SRPHelper.renderPipelineType;
|
||||
return rp == SRPHelper.RenderPipeline.URP || rp == SRPHelper.RenderPipeline.HDRP;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Actual Rendering Mode used on the current platform
|
||||
/// </summary>
|
||||
public RenderingMode actualRenderingMode
|
||||
{
|
||||
get
|
||||
{
|
||||
#pragma warning disable 0162 // warning CS0162: Unreachable code detected
|
||||
if (renderingMode == RenderingMode.GPUInstancing && !BatchingHelper.isGpuInstancingSupported) return RenderingMode.SinglePass;
|
||||
#pragma warning restore 0162
|
||||
if (renderingMode == RenderingMode.SRPBatcher && !IsSRPBatcherSupported()) return RenderingMode.SinglePass;
|
||||
|
||||
if (renderPipeline != RenderPipeline.BuiltIn)
|
||||
{
|
||||
// Using a Scriptable Render Pipeline with 'Multi-Pass' Rendering Mode is not supported
|
||||
if (renderingMode == RenderingMode.MultiPass) return RenderingMode.SinglePass;
|
||||
}
|
||||
return renderingMode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Depending on the actual Rendering Mode used, returns true if the single pass shader will be used, false otherwise.
|
||||
/// </summary>
|
||||
public bool useSinglePassShader { get { return actualRenderingMode != RenderingMode.MultiPass; } }
|
||||
|
||||
public bool requiresDoubleSidedMesh { get { return useSinglePassShader; } }
|
||||
|
||||
/// <summary>
|
||||
/// Main shader applied to the cone beam geometry
|
||||
/// </summary>
|
||||
public Shader beamShader
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if(_BeamShader == null)
|
||||
RefreshShader(RefreshShaderFlags.All);
|
||||
#endif
|
||||
return _BeamShader;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Depending on the quality of your screen, you might see some artifacts with high contrast visual (like a white beam over a black background).
|
||||
/// These is a very common problem known as color banding.
|
||||
/// To help with this issue, the plugin offers a Dithering factor: it smooths the banding by introducing a subtle pattern of noise.
|
||||
/// </summary>
|
||||
public float ditheringFactor = Consts.Config.DitheringFactor;
|
||||
|
||||
/// <summary>
|
||||
/// Number of Sides of the shared cone mesh
|
||||
/// </summary>
|
||||
public int sharedMeshSides = Consts.Config.SharedMeshSides;
|
||||
|
||||
/// <summary>
|
||||
/// Number of Segments of the shared cone mesh
|
||||
/// </summary>
|
||||
public int sharedMeshSegments = Consts.Config.SharedMeshSegments;
|
||||
|
||||
/// <summary>
|
||||
/// Global 3D Noise texture scaling: higher scale make the noise more visible, but potentially less realistic.
|
||||
/// </summary>
|
||||
[Range(Consts.Beam.NoiseScaleMin, Consts.Beam.NoiseScaleMax)]
|
||||
public float globalNoiseScale = Consts.Beam.NoiseScaleDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Global World Space direction and speed of the noise scrolling, simulating the fog/smoke movement
|
||||
/// </summary>
|
||||
public Vector3 globalNoiseVelocity = Consts.Beam.NoiseVelocityDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Tag used to retrieve the camera used to compute the fade out factor on beams
|
||||
/// </summary>
|
||||
public string fadeOutCameraTag = Consts.Config.FadeOutCameraTagDefault;
|
||||
|
||||
public Transform fadeOutCameraTransform
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_CachedFadeOutCamera == null)
|
||||
{
|
||||
ForceUpdateFadeOutCamera();
|
||||
}
|
||||
|
||||
return m_CachedFadeOutCamera;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this function if you want to manually change the fadeOutCameraTag property at runtime
|
||||
/// </summary>
|
||||
public void ForceUpdateFadeOutCamera()
|
||||
{
|
||||
var gao = GameObject.FindGameObjectWithTag(fadeOutCameraTag);
|
||||
if (gao)
|
||||
m_CachedFadeOutCamera = gao.transform;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 3D Texture storing noise data.
|
||||
/// </summary>
|
||||
[HighlightNull]
|
||||
public Texture3D noiseTexture3D = null;
|
||||
|
||||
/// <summary>
|
||||
/// ParticleSystem prefab instantiated for the Volumetric Dust Particles feature (Unity 5.5 or above)
|
||||
/// </summary>
|
||||
[HighlightNull]
|
||||
public ParticleSystem dustParticlesPrefab = null;
|
||||
|
||||
/// <summary>
|
||||
/// Noise texture for dithering feature
|
||||
/// </summary>
|
||||
public Texture2D ditheringNoiseTexture = null;
|
||||
|
||||
/// <summary>
|
||||
/// Off: do not support having a gradient as color.
|
||||
/// High Only: support gradient color only for devices with Shader Level = 35 or higher.
|
||||
/// High and Low: support gradient color for all devices.
|
||||
/// </summary>
|
||||
public FeatureEnabledColorGradient featureEnabledColorGradient = Consts.Config.FeatureEnabledColorGradientDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Support 'Soft Intersection with Opaque Geometry' feature or not.
|
||||
/// </summary>
|
||||
public bool featureEnabledDepthBlend = Consts.Config.FeatureEnabledDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Support 'Noise 3D' feature or not.
|
||||
/// </summary>
|
||||
public bool featureEnabledNoise3D = Consts.Config.FeatureEnabledDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Support 'Dynamic Occlusion' features or not.
|
||||
/// </summary>
|
||||
public bool featureEnabledDynamicOcclusion = Consts.Config.FeatureEnabledDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Support 'Mesh Skewing' feature or not.
|
||||
/// </summary>
|
||||
public bool featureEnabledMeshSkewing = Consts.Config.FeatureEnabledDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Support 'Shader Accuracy' property set to 'High' or not.
|
||||
/// </summary>
|
||||
public bool featureEnabledShaderAccuracyHigh = Consts.Config.FeatureEnabledDefault;
|
||||
|
||||
// INTERNAL
|
||||
#pragma warning disable 0414
|
||||
[SerializeField] int pluginVersion = -1;
|
||||
[SerializeField] Material _DummyMaterial = null;
|
||||
#pragma warning restore 0414
|
||||
|
||||
[SerializeField] Shader _BeamShader = null;
|
||||
Transform m_CachedFadeOutCamera = null;
|
||||
|
||||
public bool hasRenderPipelineMismatch { get { return (SRPHelper.renderPipelineType == SRPHelper.RenderPipeline.BuiltIn) != (_RenderPipeline == RenderPipeline.BuiltIn); } }
|
||||
|
||||
[RuntimeInitializeOnLoadMethod]
|
||||
static void OnStartup()
|
||||
{
|
||||
Instance.m_CachedFadeOutCamera = null;
|
||||
Instance.RefreshGlobalShaderProperties();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
Instance.RefreshShader(RefreshShaderFlags.All);
|
||||
#endif
|
||||
|
||||
if(Instance.hasRenderPipelineMismatch)
|
||||
Debug.LogError("It looks like the 'Render Pipeline' is not correctly set in the config. Please make sure to select the proper value depending on your pipeline in use.", Instance);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
geometryOverrideLayer = Consts.Config.GeometryOverrideLayerDefault;
|
||||
geometryLayerID = Consts.Config.GeometryLayerIDDefault;
|
||||
geometryTag = Consts.Config.GeometryTagDefault;
|
||||
geometryRenderQueue = (int)Consts.Config.GeometryRenderQueueDefault;
|
||||
|
||||
sharedMeshSides = Consts.Config.SharedMeshSides;
|
||||
sharedMeshSegments = Consts.Config.SharedMeshSegments;
|
||||
|
||||
globalNoiseScale = Consts.Beam.NoiseScaleDefault;
|
||||
globalNoiseVelocity = Consts.Beam.NoiseVelocityDefault;
|
||||
|
||||
renderPipeline = Consts.Config.GeometryRenderPipelineDefault;
|
||||
renderingMode = Consts.Config.GeometryRenderingModeDefault;
|
||||
ditheringFactor = Consts.Config.DitheringFactor;
|
||||
|
||||
fadeOutCameraTag = Consts.Config.FadeOutCameraTagDefault;
|
||||
|
||||
featureEnabledColorGradient = Consts.Config.FeatureEnabledColorGradientDefault;
|
||||
featureEnabledDepthBlend = Consts.Config.FeatureEnabledDefault;
|
||||
featureEnabledNoise3D = Consts.Config.FeatureEnabledDefault;
|
||||
featureEnabledDynamicOcclusion = Consts.Config.FeatureEnabledDefault;
|
||||
featureEnabledMeshSkewing = Consts.Config.FeatureEnabledDefault;
|
||||
featureEnabledShaderAccuracyHigh = Consts.Config.FeatureEnabledDefault;
|
||||
|
||||
ResetInternalData();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
GlobalMesh.Destroy();
|
||||
VolumetricLightBeam._EditorSetAllMeshesDirty();
|
||||
#endif
|
||||
}
|
||||
|
||||
void RefreshGlobalShaderProperties()
|
||||
{
|
||||
Shader.SetGlobalFloat(ShaderProperties.GlobalUsesReversedZBuffer, SystemInfo.usesReversedZBuffer ? 1.0f : 0.0f);
|
||||
Shader.SetGlobalFloat(ShaderProperties.GlobalDitheringFactor, ditheringFactor);
|
||||
Shader.SetGlobalTexture(ShaderProperties.GlobalDitheringNoiseTex, ditheringNoiseTexture);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public void _EditorSetRenderingModeAndRefreshShader(RenderingMode mode)
|
||||
{
|
||||
renderingMode = mode;
|
||||
RefreshShader(RefreshShaderFlags.All);
|
||||
}
|
||||
|
||||
void OnValidate()
|
||||
{
|
||||
sharedMeshSides = Mathf.Clamp(sharedMeshSides, Consts.Beam.GeomSidesMin, Consts.Beam.GeomSidesMax);
|
||||
sharedMeshSegments = Mathf.Clamp(sharedMeshSegments, Consts.Beam.GeomSegmentsMin, Consts.Beam.GeomSegmentsMax);
|
||||
|
||||
ditheringFactor = Mathf.Clamp01(ditheringFactor);
|
||||
}
|
||||
|
||||
void AutoSelectRenderPipeline()
|
||||
{
|
||||
var newPipeline = renderPipeline;
|
||||
switch (SRPHelper.renderPipelineType)
|
||||
{
|
||||
case SRPHelper.RenderPipeline.BuiltIn:
|
||||
newPipeline = RenderPipeline.BuiltIn;
|
||||
break;
|
||||
case SRPHelper.RenderPipeline.HDRP:
|
||||
newPipeline = RenderPipeline.HDRP;
|
||||
break;
|
||||
case SRPHelper.RenderPipeline.URP:
|
||||
case SRPHelper.RenderPipeline.LWRP:
|
||||
newPipeline = RenderPipeline.URP;
|
||||
break;
|
||||
}
|
||||
|
||||
if (newPipeline != renderPipeline)
|
||||
{
|
||||
renderPipeline = newPipeline;
|
||||
EditorUtility.SetDirty(this); // make sure to save this property change
|
||||
RefreshShader(RefreshShaderFlags.All);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EditorSelectInstance()
|
||||
{
|
||||
Selection.activeObject = Config.Instance; // this will create the instance if it doesn't exist
|
||||
if (Selection.activeObject == null)
|
||||
Debug.LogError("Cannot find any Config resource");
|
||||
}
|
||||
|
||||
[System.Flags]
|
||||
public enum RefreshShaderFlags
|
||||
{
|
||||
Reference = 1 << 1,
|
||||
Dummy = 1 << 2,
|
||||
All = Reference | Dummy,
|
||||
}
|
||||
|
||||
public void RefreshShader(RefreshShaderFlags flags)
|
||||
{
|
||||
if (flags.HasFlag(RefreshShaderFlags.Reference))
|
||||
{
|
||||
var prevShader = _BeamShader;
|
||||
|
||||
var enabledFeatures = new ShaderGenerator.EnabledFeatures
|
||||
{
|
||||
dithering = ditheringFactor > 0.0f,
|
||||
depthBlend = featureEnabledDepthBlend,
|
||||
noise3D = featureEnabledNoise3D,
|
||||
colorGradient = featureEnabledColorGradient,
|
||||
dynamicOcclusion = featureEnabledDynamicOcclusion,
|
||||
meshSkewing = featureEnabledMeshSkewing,
|
||||
shaderAccuracyHigh = featureEnabledShaderAccuracyHigh
|
||||
};
|
||||
|
||||
_BeamShader = ShaderGenerator.Generate(_RenderPipeline, actualRenderingMode, enabledFeatures);
|
||||
if (_BeamShader != prevShader)
|
||||
{
|
||||
EditorUtility.SetDirty(this);
|
||||
}
|
||||
}
|
||||
|
||||
if (flags.HasFlag(RefreshShaderFlags.Dummy) && _BeamShader != null)
|
||||
{
|
||||
bool gpuInstanced = actualRenderingMode == RenderingMode.GPUInstancing;
|
||||
_DummyMaterial = DummyMaterial.Create(_BeamShader, gpuInstanced);
|
||||
}
|
||||
|
||||
if (_DummyMaterial == null)
|
||||
{
|
||||
Debug.LogError("No dummy material referenced to VLB config, please try to reset this asset.", this);
|
||||
}
|
||||
|
||||
RefreshGlobalShaderProperties();
|
||||
}
|
||||
#endif // UNITY_EDITOR
|
||||
|
||||
public void ResetInternalData()
|
||||
{
|
||||
noiseTexture3D = Resources.Load("Noise3D_64x64x64") as Texture3D;
|
||||
|
||||
dustParticlesPrefab = Resources.Load("DustParticles", typeof(ParticleSystem)) as ParticleSystem;
|
||||
|
||||
ditheringNoiseTexture = Resources.Load("VLBDitheringNoise", typeof(Texture2D)) as Texture2D;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
RefreshShader(RefreshShaderFlags.All);
|
||||
#endif
|
||||
}
|
||||
|
||||
public ParticleSystem NewVolumetricDustParticles()
|
||||
{
|
||||
if (!dustParticlesPrefab)
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
Debug.LogError("Failed to instantiate VolumetricDustParticles prefab.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
var instance = Instantiate(dustParticlesPrefab);
|
||||
instance.useAutoRandomSeed = false;
|
||||
instance.name = "Dust Particles";
|
||||
instance.gameObject.hideFlags = Consts.Internal.ProceduralObjectsHideFlags;
|
||||
instance.gameObject.SetActive(true);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
HandleBackwardCompatibility(pluginVersion, Version.Current);
|
||||
pluginVersion = Version.Current;
|
||||
}
|
||||
|
||||
void HandleBackwardCompatibility(int serializedVersion, int newVersion)
|
||||
{
|
||||
if (serializedVersion == -1) return; // freshly new spawned config: nothing to do
|
||||
if (serializedVersion == newVersion) return; // same version: nothing to do
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (serializedVersion < 1830)
|
||||
{
|
||||
AutoSelectRenderPipeline();
|
||||
}
|
||||
|
||||
if (serializedVersion < 1950)
|
||||
{
|
||||
ResetInternalData(); // retrieve Noise3D texture converted from binary data to texture 3D asset in 1950
|
||||
EditorUtility.SetDirty(this); // make sure to save this property change
|
||||
}
|
||||
|
||||
if (newVersion > serializedVersion)
|
||||
{
|
||||
// Import to keep, we have to regenerate the shader each time the plugin is updated
|
||||
RefreshShader(RefreshShaderFlags.All);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Singleton management
|
||||
static Config ms_Instance = null;
|
||||
public static Config Instance { get { return GetInstance(true); } }
|
||||
|
||||
#if UNITY_EDITOR
|
||||
static bool ms_IsCreatingInstance = false;
|
||||
|
||||
public bool IsCurrentlyUsedInstance() { return Instance == this; }
|
||||
|
||||
public bool HasValidAssetName()
|
||||
{
|
||||
if (name.IndexOf(ConfigOverride.kAssetName) != 0)
|
||||
return false;
|
||||
|
||||
return PlatformHelper.IsValidPlatformSuffix(GetAssetSuffix());
|
||||
}
|
||||
|
||||
public string GetAssetSuffix()
|
||||
{
|
||||
var fullname = name;
|
||||
var strToFind = ConfigOverride.kAssetName;
|
||||
if (fullname.IndexOf(strToFind) == 0) return fullname.Substring(strToFind.Length);
|
||||
else return "";
|
||||
}
|
||||
#endif
|
||||
|
||||
private static Config GetInstance(bool assertIfNotFound)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
// Do not cache the instance during editing in order to handle new asset created or moved.
|
||||
if (!Application.isPlaying || ms_Instance == null)
|
||||
#else
|
||||
if (ms_Instance == null)
|
||||
#endif
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (ms_IsCreatingInstance)
|
||||
{
|
||||
Debug.LogError(string.Format("Trying to access Config.Instance while creating it. Breaking before infinite loop."));
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
{
|
||||
var newInstance = Resources.Load<ConfigOverride>(ConfigOverride.kAssetName + PlatformHelper.GetCurrentPlatformSuffix());
|
||||
|
||||
if (newInstance == null)
|
||||
newInstance = Resources.Load<ConfigOverride>(ConfigOverride.kAssetName);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (newInstance && newInstance != ms_Instance)
|
||||
{
|
||||
ms_Instance = newInstance;
|
||||
newInstance.RefreshGlobalShaderProperties(); // make sure noise textures are properly loaded as soon as the editor is started
|
||||
}
|
||||
#endif
|
||||
|
||||
ms_Instance = newInstance;
|
||||
}
|
||||
|
||||
if (ms_Instance == null)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
ms_IsCreatingInstance = true;
|
||||
ms_Instance = ConfigOverride.CreateInstanceOverride();
|
||||
ms_IsCreatingInstance = false;
|
||||
|
||||
ms_Instance.AutoSelectRenderPipeline();
|
||||
|
||||
if (Application.isPlaying)
|
||||
ms_Instance.Reset(); // Reset is not automatically when instancing a ScriptableObject when in playmode
|
||||
#endif
|
||||
Debug.Assert(!(assertIfNotFound && ms_Instance == null), string.Format("Can't find any resource of type '{0}'. Make sure you have a ScriptableObject of this type in a 'Resources' folder.", typeof(Config)));
|
||||
}
|
||||
}
|
||||
|
||||
return ms_Instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fed3f3d91454db34f894abc9ea1d8ec4
|
||||
timeCreated: 1508325517
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,37 @@
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
[HelpURL(Consts.Help.UrlConfig)]
|
||||
public class ConfigOverride : Config // useless override, only useful for backward compatibility
|
||||
{
|
||||
public const string kAssetName = "VLBConfigOverride";
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public static void CreateFolderAndAsset(Object obj, string folderParent, string folderResources, string assetName)
|
||||
{
|
||||
if (!AssetDatabase.IsValidFolder(string.Format("{0}/{1}", folderParent, folderResources)))
|
||||
AssetDatabase.CreateFolder(folderParent, folderResources);
|
||||
|
||||
CreateAsset(obj, string.Format("{0}/{1}/{2}", folderParent, folderResources, assetName));
|
||||
}
|
||||
|
||||
public static void CreateAsset(Object obj, string fullPath)
|
||||
{
|
||||
AssetDatabase.CreateAsset(obj, fullPath);
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
|
||||
public static ConfigOverride CreateInstanceOverride()
|
||||
{
|
||||
var asset = CreateInstance<ConfigOverride>();
|
||||
Debug.Assert(asset != null);
|
||||
CreateFolderAndAsset(asset, "Assets", "Resources", kAssetName + ".asset");
|
||||
return asset;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9536b6488d9e024db1c753e8e90657e
|
||||
timeCreated: 1556445627
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,153 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
public static class Consts
|
||||
{
|
||||
public const string PluginFolder = "Plugins/VolumetricLightBeam";
|
||||
|
||||
public static class Help
|
||||
{
|
||||
const string UrlBase = "http://saladgamer.com/vlb-doc/";
|
||||
const string UrlSuffix = ".html";
|
||||
public const string UrlBeam = UrlBase + "comp-lightbeam" + UrlSuffix;
|
||||
public const string UrlDustParticles = UrlBase + "comp-dustparticles" + UrlSuffix;
|
||||
public const string UrlDynamicOcclusionRaycasting = UrlBase + "comp-dynocclusion-raycasting" + UrlSuffix;
|
||||
public const string UrlDynamicOcclusionDepthBuffer = UrlBase + "comp-dynocclusion-depthbuffer" + UrlSuffix;
|
||||
public const string UrlTriggerZone = UrlBase + "comp-triggerzone" + UrlSuffix;
|
||||
public const string UrlSkewingHandle = UrlBase + "comp-skewinghandle" + UrlSuffix;
|
||||
public const string UrlEffectFlicker = UrlBase + "comp-effect-flicker" + UrlSuffix;
|
||||
public const string UrlEffectPulse = UrlBase + "comp-effect-pulse" + UrlSuffix;
|
||||
public const string UrlConfig = UrlBase + "config" + UrlSuffix;
|
||||
}
|
||||
|
||||
public static class Internal
|
||||
{
|
||||
public static readonly bool ProceduralObjectsVisibleInEditor = true;
|
||||
public static HideFlags ProceduralObjectsHideFlags { get { return ProceduralObjectsVisibleInEditor ? (HideFlags.NotEditable | HideFlags.DontSave) : (HideFlags.HideAndDontSave); } }
|
||||
}
|
||||
|
||||
public static class Beam
|
||||
{
|
||||
public static readonly Color FlatColor = Color.white;
|
||||
public const ColorMode ColorModeDefault = ColorMode.Flat;
|
||||
|
||||
public const float IntensityDefault = 1f;
|
||||
public const float IntensityMin = 0f;
|
||||
public const float SpotAngleDefault = 35f;
|
||||
public const float SpotAngleMin = 0.1f;
|
||||
public const float SpotAngleMax = 179.9f;
|
||||
public const float ConeRadiusStart = 0.1f;
|
||||
public const MeshType GeomMeshType = MeshType.Shared;
|
||||
public const int GeomSidesDefault = 18;
|
||||
public const int GeomSidesMin = 3;
|
||||
public const int GeomSidesMax = 256;
|
||||
public const int GeomSegmentsDefault = 5;
|
||||
public const int GeomSegmentsMin = 0;
|
||||
public const int GeomSegmentsMax = 64;
|
||||
public const bool GeomCap = false;
|
||||
|
||||
public const AttenuationEquation AttenuationEquationDefault = AttenuationEquation.Quadratic;
|
||||
public const float AttenuationCustomBlending = 0.5f;
|
||||
public const float FallOffStart = 0f;
|
||||
public const float FallOffEnd = 3f;
|
||||
public const float FallOffDistancesMinThreshold = 0.01f;
|
||||
|
||||
public const float DepthBlendDistance = 2f;
|
||||
public const float CameraClippingDistance = 0.5f;
|
||||
|
||||
public const float FresnelPowMaxValue = 10f;
|
||||
public const float FresnelPow = 8f;
|
||||
|
||||
public const float GlareFrontal = 0.5f;
|
||||
public const float GlareBehind = 0.5f;
|
||||
|
||||
public const NoiseMode NoiseModeDefault = NoiseMode.Disabled;
|
||||
public const float NoiseIntensityMin = 0.0f;
|
||||
public const float NoiseIntensityMax = 1.0f;
|
||||
public const float NoiseIntensityDefault = 0.5f;
|
||||
public const float NoiseScaleMin = 0.01f;
|
||||
public const float NoiseScaleMax = 2f;
|
||||
public const float NoiseScaleDefault = 0.5f;
|
||||
|
||||
public static readonly Vector3 NoiseVelocityDefault = new Vector3(0.07f, 0.18f, 0.05f);
|
||||
|
||||
public const BlendingMode BlendingModeDefault = BlendingMode.Additive;
|
||||
public const ShaderAccuracy ShaderAccuracyDefault = ShaderAccuracy.Fast;
|
||||
|
||||
public const float FadeOutBeginDefault = -150;
|
||||
public const float FadeOutEndDefault = -200;
|
||||
public const Dimensions DimensionsDefault = Dimensions.Dim3D;
|
||||
public static readonly Vector2 TiltDefault = Vector2.zero;
|
||||
public static readonly Vector3 SkewingLocalForwardDirectionDefault = Vector3.forward;
|
||||
public const Transform ClippingPlaneTransformDefault = null;
|
||||
}
|
||||
|
||||
public static class DustParticles
|
||||
{
|
||||
public const float AlphaDefault = 0.5f;
|
||||
public const float SizeDefault = 0.01f;
|
||||
public const ParticlesDirection DirectionDefault = ParticlesDirection.Random;
|
||||
public static readonly Vector3 VelocityDefault = new Vector3(0.0f, 0.0f, 0.03f);
|
||||
public const float DensityDefault = 5f;
|
||||
public const float DensityMin = 0f;
|
||||
public const float DensityMax = 1000f;
|
||||
public static readonly MinMaxRangeFloat SpawnDistanceRangeDefault = new MinMaxRangeFloat(0.0f, 0.7f);
|
||||
public const bool CullingEnabledDefault = false;
|
||||
public const float CullingMaxDistanceDefault = 10f;
|
||||
public const float CullingMaxDistanceMin = 1f;
|
||||
}
|
||||
|
||||
public static class DynOcclusion
|
||||
{
|
||||
public static readonly LayerMask LayerMaskDefault = 1; // Default layer
|
||||
public const float FadeDistanceToSurfaceDefault = 0.25f;
|
||||
public const DynamicOcclusionUpdateRate UpdateRateDefault = DynamicOcclusionUpdateRate.EveryXFrames;
|
||||
public const int WaitFramesCountDefault = 3;
|
||||
|
||||
public const Dimensions RaycastingDimensionsDefault = Dimensions.Dim3D;
|
||||
public const bool RaycastingConsiderTriggersDefault = false;
|
||||
public const float RaycastingMinOccluderAreaDefault = 0.0f;
|
||||
public const float RaycastingMinSurfaceRatioDefault = 0.5f;
|
||||
public const float RaycastingMinSurfaceRatioMin = 50f;
|
||||
public const float RaycastingMinSurfaceRatioMax = 100f;
|
||||
public const float RaycastingMaxSurfaceDotDefault = 0.25f; // around 75 degrees
|
||||
public const float RaycastingMaxSurfaceAngleMin = 45f;
|
||||
public const float RaycastingMaxSurfaceAngleMax = 90f;
|
||||
public const PlaneAlignment RaycastingPlaneAlignmentDefault = PlaneAlignment.Surface;
|
||||
public const float RaycastingPlaneOffsetDefault = 0.1f;
|
||||
|
||||
public const int DepthBufferDepthMapResolutionDefault = 32;
|
||||
public const bool DepthBufferOcclusionCullingDefault = true;
|
||||
}
|
||||
|
||||
public static class Effects
|
||||
{
|
||||
public const EffectAbstractBase.ComponentsToChange ComponentsToChangeDefault = (EffectAbstractBase.ComponentsToChange)int.MaxValue;
|
||||
public const bool RestoreBaseIntensityDefault = true;
|
||||
public const float FrequencyDefault = 10.0f;
|
||||
public const bool PerformPausesDefault = false;
|
||||
public static readonly MinMaxRangeFloat FlickeringDurationDefault = new MinMaxRangeFloat(1.0f, 4.0f);
|
||||
public static readonly MinMaxRangeFloat PauseDurationDefault = new MinMaxRangeFloat(0.0f, 1.0f);
|
||||
public static readonly MinMaxRangeFloat IntensityAmplitudeDefault = new MinMaxRangeFloat(-1.0f, 1.0f);
|
||||
public const float SmoothingDefault = 0.05f;
|
||||
}
|
||||
|
||||
public static class Config
|
||||
{
|
||||
public const bool GeometryOverrideLayerDefault = true;
|
||||
public const int GeometryLayerIDDefault = 1;
|
||||
public const string GeometryTagDefault = "Untagged";
|
||||
public const string FadeOutCameraTagDefault = "MainCamera";
|
||||
public const RenderQueue GeometryRenderQueueDefault = RenderQueue.Transparent;
|
||||
public const RenderPipeline GeometryRenderPipelineDefault = RenderPipeline.BuiltIn;
|
||||
public const RenderingMode GeometryRenderingModeDefault = RenderingMode.SinglePass;
|
||||
public const int Noise3DSizeDefault = 64;
|
||||
public const int SharedMeshSides = 24;
|
||||
public const int SharedMeshSegments = 5;
|
||||
public const float DitheringFactor = 0.0f;
|
||||
public const bool FeatureEnabledDefault = true;
|
||||
public const FeatureEnabledColorGradient FeatureEnabledColorGradientDefault = FeatureEnabledColorGradient.HighOnly;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa337c7dd20e18b49b4b0b5b42e59e96
|
||||
timeCreated: 1508860935
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,50 @@
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
public static class DummyMaterial
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
static string GetPath(Shader shader)
|
||||
{
|
||||
const string kDummyFilename = "VLBDummyMaterial.mat";
|
||||
const string kDummyPathFallback = "Assets/" + Consts.PluginFolder + "/Shaders/" + kDummyFilename;
|
||||
|
||||
if (shader == null)
|
||||
return kDummyPathFallback;
|
||||
|
||||
var shaderPath = AssetDatabase.GetAssetPath(shader);
|
||||
if (string.IsNullOrEmpty(shaderPath))
|
||||
return kDummyPathFallback;
|
||||
|
||||
var shaderFolder = System.IO.Path.GetDirectoryName(shaderPath);
|
||||
return System.IO.Path.Combine(shaderFolder, kDummyFilename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a dummy material with the proper instancing flag to prevent from stripping away needed shader variants when exporting build
|
||||
/// </summary>
|
||||
public static Material Create(Shader shader, bool gpuInstanced)
|
||||
{
|
||||
if (shader == null)
|
||||
return null;
|
||||
|
||||
string path = GetPath(shader);
|
||||
var dummyMat = AssetDatabase.LoadAssetAtPath<Material>(path);
|
||||
if (dummyMat == null
|
||||
|| dummyMat.shader != shader
|
||||
|| BatchingHelper.IsGpuInstancingEnabled(dummyMat) != gpuInstanced)
|
||||
{
|
||||
dummyMat = MaterialManager.NewMaterialPersistent(shader, gpuInstanced);
|
||||
if (dummyMat)
|
||||
AssetDatabase.CreateAsset(dummyMat, path);
|
||||
}
|
||||
|
||||
return dummyMat;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1cb6a728dad16224884d567a0a1d618d
|
||||
timeCreated: 1546757296
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,182 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
[AddComponentMenu("")] // hide it from Component search
|
||||
[DisallowMultipleComponent]
|
||||
[RequireComponent(typeof(VolumetricLightBeam))]
|
||||
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;
|
||||
|
||||
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 VolumetricLightBeam m_Master = null;
|
||||
protected MaterialModifier.Callback m_MaterialModifierCallbackCached = null;
|
||||
|
||||
protected abstract string GetShaderKeyword();
|
||||
protected abstract MaterialManager.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<VolumetricLightBeam>();
|
||||
Debug.Assert(m_Master);
|
||||
|
||||
m_Master._INTERNAL_DynamicOcclusionMode = GetDynamicOcclusionMode();
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
m_Master._INTERNAL_DynamicOcclusionMode = MaterialManager.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: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,229 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
[HelpURL(Consts.Help.UrlDynamicOcclusionDepthBuffer)]
|
||||
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.FadeDistanceToSurfaceDefault;
|
||||
|
||||
|
||||
protected override string GetShaderKeyword() { return "VLB_OCCLUSION_DEPTH_TEXTURE"; }
|
||||
protected override MaterialManager.DynamicOcclusion GetDynamicOcclusionMode() { return MaterialManager.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 (source == ProcessOcclusionSource.RenderLoop && 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)
|
||||
{
|
||||
ProcessOcclusionInternal();
|
||||
m_NeedToUpdateOcclusionNextFrame = false;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateDepthCameraPropertiesAccordingToBeam()
|
||||
{
|
||||
Debug.Assert(m_Master);
|
||||
Debug.Assert(m_DepthCamera);
|
||||
|
||||
float apexDist = m_Master.coneApexOffsetZ;
|
||||
m_DepthCamera.transform.localPosition = m_Master.beamLocalForward * (-apexDist);
|
||||
m_DepthCamera.transform.localRotation = m_Master.beamInternalLocalRotation;
|
||||
|
||||
var scale = m_Master.lossyScale;
|
||||
if (!Mathf.Approximately(scale.y * scale.z, 0))
|
||||
{
|
||||
const float kMinNearClipPlane = 0.1f; // should be the same than in shader
|
||||
m_DepthCamera.nearClipPlane = Mathf.Max(apexDist, kMinNearClipPlane) * scale.z;
|
||||
m_DepthCamera.farClipPlane = (m_Master.maxGeometryDistance + apexDist) * scale.z;
|
||||
m_DepthCamera.aspect = scale.x / scale.y;
|
||||
m_DepthCamera.fieldOfView = scale.y * m_Master.coneAngle / scale.z;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
m_DepthCamera = new GameObject("Depth Camera").AddComponent<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);
|
||||
|
||||
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.DynamicOcclusionDepthTexture, m_DepthCamera.targetTexture);
|
||||
owner.SetMaterialProp(ShaderProperties.DynamicOcclusionDepthProps, fadeDistanceToSurface);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
bool m_NeedToReinstantiateDepthCamera = false;
|
||||
|
||||
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: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: f4140fedf3f72d7448a4e55ea9db44ab, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,472 @@
|
||||
#if DEBUG
|
||||
//#define DEBUG_SHOW_RAYCAST_LINES
|
||||
#endif
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
[HelpURL(Consts.Help.UrlDynamicOcclusionRaycasting)]
|
||||
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.FadeDistanceToSurfaceDefault;
|
||||
|
||||
[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 "VLB_OCCLUSION_CLIPPING_PLANE"; }
|
||||
protected override MaterialManager.DynamicOcclusion GetDynamicOcclusionMode() { return MaterialManager.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.lossyScale.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.DynamicOcclusionClippingPlaneWS, new Vector4(planeWS.normal.x, planeWS.normal.y, planeWS.normal.z, planeWS.distance));
|
||||
owner.SetMaterialProp(ShaderProperties.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: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: f4140fedf3f72d7448a4e55ea9db44ab, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,16 @@
|
||||
public static class EditorPrefsStrings
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
public static class Beam
|
||||
{
|
||||
public const string PrefShowTiltDir = "VLB_BEAM_SHOWTILTDIR";
|
||||
}
|
||||
|
||||
public static class DynOcclusion
|
||||
{
|
||||
public const string PrefShowDebugPlane = "VLB_DYNOCCLUSION_SHOWDEBUGPLANE";
|
||||
public const string PrefRaycastingEditor = "VLB_DYNOCCLUSION_RAYCASTINGEDITOR";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a26cf2b22b6b7cd47ab72ea8f98b0a73
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,80 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
[AddComponentMenu("")] // hide it from Component search
|
||||
[DisallowMultipleComponent]
|
||||
public class EffectAbstractBase : MonoBehaviour
|
||||
{
|
||||
public const string ClassName = "EffectAbstractBase";
|
||||
|
||||
[System.Flags]
|
||||
public enum ComponentsToChange
|
||||
{
|
||||
UnityLight = 1 << 0,
|
||||
VolumetricLightBeam = 1 << 1,
|
||||
VolumetricDustParticles = 1 << 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decide which component to change among:
|
||||
/// - Unity's Light
|
||||
/// - Volumetric Light Beam
|
||||
/// - Volumetric Dust Particles
|
||||
/// </summary>
|
||||
public ComponentsToChange componentsToChange = Consts.Effects.ComponentsToChangeDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Restore the default intensity when this component is disabled.
|
||||
/// </summary>
|
||||
public bool restoreBaseIntensity = Consts.Effects.RestoreBaseIntensityDefault;
|
||||
|
||||
|
||||
protected VolumetricLightBeam m_Beam = null;
|
||||
protected Light m_Light = null;
|
||||
protected VolumetricDustParticles m_Particles = null;
|
||||
protected float m_BaseIntensityBeamInside = 0.0f;
|
||||
protected float m_BaseIntensityBeamOutside = 0.0f;
|
||||
protected float m_BaseIntensityLight = 0.0f;
|
||||
|
||||
protected void SetAdditiveIntensity(float additive)
|
||||
{
|
||||
if (componentsToChange.HasFlag(ComponentsToChange.VolumetricLightBeam) && m_Beam)
|
||||
{
|
||||
m_Beam.intensityInside = Mathf.Max(0.0f, m_BaseIntensityBeamInside + additive);
|
||||
m_Beam.intensityOutside = Mathf.Max(0.0f, m_BaseIntensityBeamOutside + additive);
|
||||
}
|
||||
|
||||
if (componentsToChange.HasFlag(ComponentsToChange.UnityLight) && m_Light)
|
||||
m_Light.intensity = Mathf.Max(0.0f, m_BaseIntensityLight + additive);
|
||||
|
||||
if (componentsToChange.HasFlag(ComponentsToChange.VolumetricDustParticles) && m_Particles)
|
||||
m_Particles.alphaAdditionalRuntime = 1.0f + additive;
|
||||
}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
m_Beam = GetComponent<VolumetricLightBeam>();
|
||||
m_Light = GetComponent<Light>();
|
||||
m_Particles = GetComponent<VolumetricDustParticles>();
|
||||
m_BaseIntensityBeamInside = m_Beam ? m_Beam.intensityInside : 0.0f;
|
||||
m_BaseIntensityBeamOutside = m_Beam ? m_Beam.intensityOutside : 0.0f;
|
||||
m_BaseIntensityLight = m_Light ? m_Light.intensity : 0.0f;
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
StopAllCoroutines();
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
StopAllCoroutines();
|
||||
|
||||
if (restoreBaseIntensity)
|
||||
SetAdditiveIntensity(0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a25e62b0a3d171468d83a4db6928379
|
||||
timeCreated: 1617641637
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,106 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
[HelpURL(Consts.Help.UrlEffectFlicker)]
|
||||
public class EffectFlicker : EffectAbstractBase
|
||||
{
|
||||
public new const string ClassName = "EffectFlicker";
|
||||
|
||||
/// <summary>
|
||||
/// Frequency of flickering.
|
||||
/// Higher value means the flickering will occur faster.
|
||||
/// </summary>
|
||||
[Range(1.0f, 60.0f)]
|
||||
public float frequency = Consts.Effects.FrequencyDefault;
|
||||
|
||||
/// <summary>
|
||||
/// If enabled, pauses will be added between 2 flickering sequences.
|
||||
/// </summary>
|
||||
public bool performPauses = Consts.Effects.PerformPausesDefault;
|
||||
|
||||
/// <summary>
|
||||
/// The duration of a flickering sequence.
|
||||
/// A random value will be picked each time inside that range.
|
||||
/// </summary>
|
||||
[MinMaxRange(0.0f, 10.0f)]
|
||||
public MinMaxRangeFloat flickeringDuration = Consts.Effects.FlickeringDurationDefault;
|
||||
|
||||
/// <summary>
|
||||
/// The duration of a pause sequence.
|
||||
/// A random value will be picked each time inside that range.
|
||||
/// </summary>
|
||||
[MinMaxRange(0.0f, 10.0f)]
|
||||
public MinMaxRangeFloat pauseDuration = Consts.Effects.PauseDurationDefault;
|
||||
|
||||
/// <summary>
|
||||
/// The amplitude of intensity change which will be applied to the Light and/or Beam.
|
||||
/// A random value will be picked each time inside that range.
|
||||
/// </summary>
|
||||
[MinMaxRange(-5.0f, 5.0f)]
|
||||
public MinMaxRangeFloat intensityAmplitude = Consts.Effects.IntensityAmplitudeDefault;
|
||||
|
||||
/// <summary>
|
||||
/// How much intensity change will be smoothed.
|
||||
/// Higher value means the more smoothing.
|
||||
/// </summary>
|
||||
[Range(0.0f, 0.25f)]
|
||||
public float smoothing = Consts.Effects.SmoothingDefault;
|
||||
|
||||
|
||||
float m_CurrentAdditiveIntensity = 0.0f;
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
StartCoroutine(CoUpdate());
|
||||
}
|
||||
|
||||
IEnumerator CoUpdate()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
yield return CoFlicker();
|
||||
|
||||
float remaining = pauseDuration.randomValue;
|
||||
do
|
||||
{
|
||||
remaining -= Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
while (performPauses && remaining > 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator CoFlicker()
|
||||
{
|
||||
float remainingDuration = flickeringDuration.randomValue;
|
||||
float lastTime = Time.deltaTime;
|
||||
|
||||
while (!performPauses || remainingDuration > 0.0f)
|
||||
{
|
||||
Debug.Assert(frequency > 0.0f);
|
||||
float freqDuration = 1.0f / frequency;
|
||||
yield return CoChangeIntensity(freqDuration, intensityAmplitude.randomValue);
|
||||
remainingDuration -= freqDuration;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator CoChangeIntensity(float expectedDuration, float nextIntensity)
|
||||
{
|
||||
float velocity = 0.0f;
|
||||
float t = 0.0f;
|
||||
|
||||
while (t < expectedDuration)
|
||||
{
|
||||
m_CurrentAdditiveIntensity = Mathf.SmoothDamp(m_CurrentAdditiveIntensity, nextIntensity, ref velocity, smoothing);
|
||||
SetAdditiveIntensity(m_CurrentAdditiveIntensity);
|
||||
t += Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: de6c986ea168387458edac98bcd43dfd
|
||||
timeCreated: 1617798048
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 6dfec383bc9ad504891027fdab8062b1, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
[HelpURL(Consts.Help.UrlEffectPulse)]
|
||||
public class EffectPulse : EffectAbstractBase
|
||||
{
|
||||
public new const string ClassName = "EffectPulse";
|
||||
|
||||
/// <summary>
|
||||
/// Frequency of pulsing.
|
||||
/// Higher value means the pulsing will occur faster.
|
||||
/// </summary>
|
||||
[Range(0.1f, 60.0f)]
|
||||
public float frequency = Consts.Effects.FrequencyDefault;
|
||||
|
||||
/// <summary>
|
||||
/// The amplitude of intensity change which will be applied to the Light and/or Beam.
|
||||
/// A random value will be picked each time inside that range.
|
||||
/// </summary>
|
||||
[MinMaxRange(-5.0f, 5.0f)]
|
||||
public MinMaxRangeFloat intensityAmplitude = Consts.Effects.IntensityAmplitudeDefault;
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
StartCoroutine(CoUpdate());
|
||||
}
|
||||
|
||||
IEnumerator CoUpdate()
|
||||
{
|
||||
var t = 0.0f;
|
||||
while (true)
|
||||
{
|
||||
var cos = Mathf.Sin(frequency * t);
|
||||
var value = intensityAmplitude.GetLerpedValue(cos * 0.5f + 0.5f);
|
||||
SetAdditiveIntensity(value);
|
||||
|
||||
yield return null;
|
||||
t += Time.deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d4d69db65d6d47040b7bd02d847008da
|
||||
timeCreated: 1617798061
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 6dfec383bc9ad504891027fdab8062b1, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,136 @@
|
||||
namespace VLB
|
||||
{
|
||||
public enum FeatureEnabledColorGradient
|
||||
{
|
||||
Off, // Do not support having a gradient as color
|
||||
HighOnly, // Support gradient color only for devices with Shader Level = 35 or higher
|
||||
HighAndLow // Support gradient color for all devices
|
||||
};
|
||||
|
||||
public enum ColorMode
|
||||
{
|
||||
Flat, // Apply a flat/plain/single color
|
||||
Gradient // Apply a gradient
|
||||
}
|
||||
|
||||
public enum AttenuationEquation
|
||||
{
|
||||
Linear = 0, // Simple linear attenuation.
|
||||
Quadratic = 1, // Quadratic attenuation, which usually gives more realistic results.
|
||||
Blend = 2 // Custom blending mix between linear and quadratic attenuation formulas. Use attenuationEquation property to tweak the mix.
|
||||
}
|
||||
|
||||
public enum BlendingMode
|
||||
{
|
||||
Additive,
|
||||
SoftAdditive,
|
||||
TraditionalTransparency,
|
||||
}
|
||||
|
||||
public enum ShaderAccuracy
|
||||
{
|
||||
/// <summary> Default accuracy: a lot of computation are done on the vertex shader to maximize performance. </summary>
|
||||
Fast,
|
||||
/// <summary> Higher accuracy: most of the computation are done on the pixel shader to maximize graphical quality at some performance cost. </summary>
|
||||
High,
|
||||
}
|
||||
|
||||
public enum NoiseMode
|
||||
{
|
||||
/// <summary> 3D Noise is disabled </summary>
|
||||
Disabled,
|
||||
/// <summary> 3D Noise is enabled: noise will look static compared to the world </summary>
|
||||
WorldSpace,
|
||||
/// <summary> 3D Noise is enabled: noise will look static compared to the beam position </summary>
|
||||
LocalSpace,
|
||||
}
|
||||
|
||||
public enum MeshType
|
||||
{
|
||||
Shared, // Use the global shared mesh (recommended setting, since it will save a lot on memory). Will use the geometry properties set on Config.
|
||||
Custom, // Use a custom mesh instead. Will use the geometry properties set on the beam.
|
||||
}
|
||||
|
||||
public enum RenderPipeline
|
||||
{
|
||||
/// <summary> Unity's built-in Render Pipeline. </summary>
|
||||
BuiltIn,
|
||||
/// <summary> Use the Universal Render Pipeline. </summary>
|
||||
URP,
|
||||
/// <summary> Use the High Definition Render Pipeline. </summary>
|
||||
HDRP,
|
||||
}
|
||||
|
||||
public enum RenderingMode
|
||||
{
|
||||
/// <summary> Use the 2 pass shader. Will generate 2 drawcalls per beam (Not compatible with custom Render Pipeline such as HDRP and LWRP).</summary>
|
||||
MultiPass,
|
||||
/// <summary> Use the 1 pass shader. Will generate 1 drawcall per beam. </summary>
|
||||
SinglePass,
|
||||
/// <summary> Dynamically batch multiple beams to combine and reduce draw calls. </summary>
|
||||
GPUInstancing,
|
||||
/// <summary> Use the SRP Batcher to automatically batch multiple beams and reduce draw calls. Only available when using SRP. </summary>
|
||||
SRPBatcher,
|
||||
}
|
||||
|
||||
public enum RenderQueue
|
||||
{
|
||||
/// Specify a custom render queue.
|
||||
Custom = 0,
|
||||
|
||||
/// This render queue is rendered before any others.
|
||||
Background = 1000,
|
||||
|
||||
/// Opaque geometry uses this queue.
|
||||
Geometry = 2000,
|
||||
|
||||
/// Alpha tested geometry uses this queue.
|
||||
AlphaTest = 2450,
|
||||
|
||||
/// Last render queue that is considered "opaque".
|
||||
GeometryLast = 2500,
|
||||
|
||||
/// This render queue is rendered after Geometry and AlphaTest, in back-to-front order.
|
||||
Transparent = 3000,
|
||||
|
||||
/// This render queue is meant for overlay effects.
|
||||
Overlay = 4000,
|
||||
}
|
||||
|
||||
public enum Dimensions
|
||||
{
|
||||
/// <summary> 3D </summary>
|
||||
Dim3D,
|
||||
|
||||
/// <summary> 2D </summary>
|
||||
Dim2D
|
||||
}
|
||||
|
||||
public enum PlaneAlignment
|
||||
{
|
||||
/// <summary>Align the plane to the surface normal which blocks the beam. Works better for large occluders such as floors and walls.</summary>
|
||||
Surface,
|
||||
/// <summary>Keep the plane aligned with the beam direction. Works better with more complex occluders or with corners.</summary>
|
||||
Beam
|
||||
}
|
||||
|
||||
[System.Flags]
|
||||
public enum DynamicOcclusionUpdateRate
|
||||
{
|
||||
Never = 1 << 0,
|
||||
OnEnable = 1 << 1,
|
||||
OnBeamMove = 1 << 2,
|
||||
EveryXFrames = 1 << 3,
|
||||
OnBeamMoveAndEveryXFrames = OnBeamMove | EveryXFrames,
|
||||
}
|
||||
|
||||
public enum ParticlesDirection
|
||||
{
|
||||
/// <summary> Random direction. </summary>
|
||||
Random,
|
||||
/// <summary> Particles follow the velicity direction in local space (Z is along the beam). </summary>
|
||||
LocalSpace,
|
||||
/// <summary> Particles follow the velicity direction in world space. </summary>
|
||||
WorldSpace
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 368667f41a4ca5a48bb73bc39c67ab69
|
||||
timeCreated: 1521635218
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,45 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
public static class GlobalMesh
|
||||
{
|
||||
public static Mesh Get()
|
||||
{
|
||||
var needDoubleSided = Config.Instance.requiresDoubleSidedMesh;
|
||||
|
||||
if (ms_Mesh == null
|
||||
|| ms_DoubleSided != needDoubleSided)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
ms_Mesh = MeshGenerator.GenerateConeZ_Radius(
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e4bae9c762e9004296c04beb33798ed
|
||||
timeCreated: 1529559345
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,33 @@
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
/// <summary>
|
||||
/// Highlight in red in inspector in not set
|
||||
/// </summary>
|
||||
public sealed class HighlightNullAttribute : PropertyAttribute { }
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[CustomPropertyDrawer(typeof(HighlightNullAttribute))]
|
||||
public class HighlightNullDrawer : PropertyDrawer
|
||||
{
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
if (property.propertyType != SerializedPropertyType.ObjectReference)
|
||||
{
|
||||
EditorGUI.LabelField(position, label.text, "Only valid for object references");
|
||||
return;
|
||||
}
|
||||
|
||||
if (property.objectReferenceValue == null)
|
||||
EditorGUI.DrawRect(position, Color.red);
|
||||
|
||||
EditorGUI.ObjectField(position, property, label);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e7b5213963066994eaa8a96b953deb97
|
||||
timeCreated: 1511460052
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,191 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
public static class MaterialManager
|
||||
{
|
||||
public static MaterialPropertyBlock materialPropertyBlock = new MaterialPropertyBlock();
|
||||
|
||||
public enum BlendingMode
|
||||
{
|
||||
Additive,
|
||||
SoftAdditive,
|
||||
TraditionalTransparency,
|
||||
Count
|
||||
}
|
||||
|
||||
public enum Noise3D
|
||||
{
|
||||
Off,
|
||||
On,
|
||||
Count
|
||||
}
|
||||
|
||||
public enum DepthBlend
|
||||
{
|
||||
Off,
|
||||
On,
|
||||
Count
|
||||
}
|
||||
|
||||
public enum ColorGradient
|
||||
{
|
||||
Off,
|
||||
MatrixLow,
|
||||
MatrixHigh,
|
||||
Count
|
||||
}
|
||||
|
||||
public enum DynamicOcclusion
|
||||
{
|
||||
Off,
|
||||
ClippingPlane,
|
||||
DepthTexture,
|
||||
Count
|
||||
}
|
||||
|
||||
public enum MeshSkewing
|
||||
{
|
||||
Off,
|
||||
On,
|
||||
Count
|
||||
}
|
||||
|
||||
public enum ShaderAccuracy
|
||||
{
|
||||
Fast,
|
||||
High,
|
||||
Count
|
||||
}
|
||||
|
||||
static readonly UnityEngine.Rendering.BlendMode[] BlendingMode_SrcFactor = new UnityEngine.Rendering.BlendMode[(int)BlendingMode.Count]
|
||||
{
|
||||
UnityEngine.Rendering.BlendMode.One, // Additive
|
||||
UnityEngine.Rendering.BlendMode.OneMinusDstColor, // SoftAdditive
|
||||
UnityEngine.Rendering.BlendMode.SrcAlpha, // TraditionalTransparency
|
||||
};
|
||||
|
||||
static readonly UnityEngine.Rendering.BlendMode[] BlendingMode_DstFactor = new UnityEngine.Rendering.BlendMode[(int)BlendingMode.Count]
|
||||
{
|
||||
UnityEngine.Rendering.BlendMode.One, // Additive
|
||||
UnityEngine.Rendering.BlendMode.One, // SoftAdditive
|
||||
UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha, // TraditionalTransparency
|
||||
};
|
||||
|
||||
static readonly bool[] BlendingMode_AlphaAsBlack = new bool[(int)BlendingMode.Count]
|
||||
{
|
||||
true, // Additive
|
||||
true, // SoftAdditive
|
||||
false, // TraditionalTransparency
|
||||
};
|
||||
|
||||
static int kStaticPropertiesCount = (int)BlendingMode.Count * (int)Noise3D.Count * (int)DepthBlend.Count * (int)ColorGradient.Count * (int)DynamicOcclusion.Count * (int)MeshSkewing.Count * (int)ShaderAccuracy.Count;
|
||||
|
||||
public struct StaticProperties
|
||||
{
|
||||
public BlendingMode blendingMode;
|
||||
public Noise3D noise3D;
|
||||
public DepthBlend depthBlend;
|
||||
public ColorGradient colorGradient;
|
||||
public DynamicOcclusion dynamicOcclusion;
|
||||
public MeshSkewing meshSkewing;
|
||||
public ShaderAccuracy shaderAccuracy;
|
||||
|
||||
int blendingModeID { get { return (int)blendingMode; } }
|
||||
int noise3DID { get { return Config.Instance.featureEnabledNoise3D ? (int)noise3D : 0; } }
|
||||
int depthBlendID { get { return Config.Instance.featureEnabledDepthBlend ? (int)depthBlend : 0; } }
|
||||
int colorGradientID { get { return Config.Instance.featureEnabledColorGradient != FeatureEnabledColorGradient.Off ? (int)colorGradient : 0; } }
|
||||
int dynamicOcclusionID { get { return Config.Instance.featureEnabledDynamicOcclusion ? (int)dynamicOcclusion : 0; } }
|
||||
int meshSkewingID { get { return Config.Instance.featureEnabledMeshSkewing ? (int)meshSkewing : 0; } }
|
||||
int shaderAccuracyID { get { return Config.Instance.featureEnabledShaderAccuracyHigh ? (int)shaderAccuracy : 0; } }
|
||||
|
||||
public int materialID
|
||||
{
|
||||
get
|
||||
{
|
||||
return (((((((blendingModeID)
|
||||
* (int)Noise3D.Count + noise3DID)
|
||||
* (int)DepthBlend.Count + depthBlendID)
|
||||
* (int)ColorGradient.Count + colorGradientID)
|
||||
* (int)DynamicOcclusion.Count + dynamicOcclusionID)
|
||||
* (int)MeshSkewing.Count + meshSkewingID)
|
||||
* (int)ShaderAccuracy.Count + shaderAccuracyID)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyToMaterial(Material mat)
|
||||
{
|
||||
mat.SetKeywordEnabled(ShaderKeywords.AlphaAsBlack, BlendingMode_AlphaAsBlack[(int)blendingMode]);
|
||||
mat.SetKeywordEnabled(ShaderKeywords.ColorGradientMatrixLow, colorGradient == ColorGradient.MatrixLow);
|
||||
mat.SetKeywordEnabled(ShaderKeywords.ColorGradientMatrixHigh, colorGradient == ColorGradient.MatrixHigh);
|
||||
mat.SetKeywordEnabled(ShaderKeywords.DepthBlend, depthBlend == DepthBlend.On);
|
||||
mat.SetKeywordEnabled(ShaderKeywords.Noise3D, noise3D == Noise3D.On);
|
||||
mat.SetKeywordEnabled(ShaderKeywords.OcclusionClippingPlane, dynamicOcclusion == DynamicOcclusion.ClippingPlane);
|
||||
mat.SetKeywordEnabled(ShaderKeywords.OcclusionDepthTexture, dynamicOcclusion == DynamicOcclusion.DepthTexture);
|
||||
mat.SetKeywordEnabled(ShaderKeywords.MeshSkewing, meshSkewing == MeshSkewing.On);
|
||||
mat.SetKeywordEnabled(ShaderKeywords.ShaderAccuracyHigh, shaderAccuracy == ShaderAccuracy.High);
|
||||
|
||||
mat.SetInt(ShaderProperties.BlendSrcFactor, (int)BlendingMode_SrcFactor[(int)blendingMode]);
|
||||
mat.SetInt(ShaderProperties.BlendDstFactor, (int)BlendingMode_DstFactor[(int)blendingMode]);
|
||||
}
|
||||
}
|
||||
|
||||
public static Material NewMaterialTransient(bool gpuInstanced)
|
||||
{
|
||||
var material = NewMaterialPersistent(Config.Instance.beamShader, gpuInstanced);
|
||||
if (material)
|
||||
{
|
||||
material.hideFlags = Consts.Internal.ProceduralObjectsHideFlags;
|
||||
material.renderQueue = Config.Instance.geometryRenderQueue;
|
||||
}
|
||||
return material;
|
||||
}
|
||||
|
||||
public static Material NewMaterialPersistent(Shader shader, bool gpuInstanced)
|
||||
{
|
||||
if (!shader)
|
||||
{
|
||||
Debug.LogError("Invalid VLB Shader. Please try to reset the VLB Config asset or reinstall the plugin.");
|
||||
return null;
|
||||
}
|
||||
|
||||
var material = new Material(shader);
|
||||
BatchingHelper.SetMaterialProperties(material, gpuInstanced);
|
||||
return material;
|
||||
}
|
||||
|
||||
class MaterialsGroup
|
||||
{
|
||||
public Material[] materials = new Material[kStaticPropertiesCount];
|
||||
}
|
||||
|
||||
static Hashtable ms_MaterialsGroup = new Hashtable(1);
|
||||
|
||||
public static Material GetInstancedMaterial(uint groupID, ref StaticProperties staticProps) // pass StaticProperties by ref to avoid per value arg copy
|
||||
{
|
||||
MaterialsGroup group = (MaterialsGroup)ms_MaterialsGroup[groupID];
|
||||
if (group == null)
|
||||
{
|
||||
group = new MaterialsGroup();
|
||||
ms_MaterialsGroup[groupID] = group;
|
||||
}
|
||||
|
||||
int matID = staticProps.materialID;
|
||||
Debug.Assert(matID < kStaticPropertiesCount);
|
||||
var mat = group.materials[matID];
|
||||
if (mat == null)
|
||||
{
|
||||
mat = NewMaterialTransient(gpuInstanced:true);
|
||||
if(mat)
|
||||
{
|
||||
group.materials[matID] = mat;
|
||||
staticProps.ApplyToMaterial(mat);
|
||||
}
|
||||
}
|
||||
|
||||
return mat;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f3b53f8b071fa74997d838b723bcfa7
|
||||
timeCreated: 1537852951
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,18 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
public static class MaterialModifier
|
||||
{
|
||||
public interface Interface
|
||||
{
|
||||
void SetMaterialProp(int nameID, float value);
|
||||
void SetMaterialProp(int nameID, Vector4 value);
|
||||
void SetMaterialProp(int nameID, Color value);
|
||||
void SetMaterialProp(int nameID, Matrix4x4 value);
|
||||
void SetMaterialProp(int nameID, Texture value);
|
||||
}
|
||||
|
||||
public delegate void Callback(Interface owner);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 51c15cec95cca374a92f3e0c0d7b7fe8
|
||||
timeCreated: 1577644734
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,258 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
public static class MeshGenerator
|
||||
{
|
||||
const float kMinTruncatedRadius = 0.001f;
|
||||
|
||||
static float GetAngleOffset(int numSides)
|
||||
{
|
||||
// rotate square beams so they are properly oriented to scale them more easily
|
||||
return numSides == 4 ? (Mathf.PI * 0.25f) : 0f;
|
||||
}
|
||||
|
||||
public static Mesh GenerateConeZ_RadiusAndAngle(float lengthZ, float radiusStart, float coneAngle, int numSides, int numSegments, bool cap, bool doubleSided)
|
||||
{
|
||||
Debug.Assert(lengthZ > 0f);
|
||||
Debug.Assert(coneAngle > 0f && coneAngle < 180f);
|
||||
var radiusEnd = lengthZ * Mathf.Tan(coneAngle * Mathf.Deg2Rad * 0.5f);
|
||||
|
||||
return GenerateConeZ_Radius(lengthZ, radiusStart, radiusEnd, numSides, numSegments, cap, doubleSided);
|
||||
}
|
||||
|
||||
public static Mesh GenerateConeZ_Angle(float lengthZ, float coneAngle, int numSides, int numSegments, bool cap, bool doubleSided)
|
||||
{
|
||||
return GenerateConeZ_RadiusAndAngle(lengthZ, 0f, coneAngle, numSides, numSegments, cap, doubleSided);
|
||||
}
|
||||
|
||||
public static Mesh GenerateConeZ_Radius(float lengthZ, float radiusStart, float radiusEnd, int numSides, int numSegments, bool cap, bool doubleSided)
|
||||
{
|
||||
Debug.Assert(lengthZ > 0f);
|
||||
Debug.Assert(radiusStart >= 0f);
|
||||
Debug.Assert(numSides >= 3);
|
||||
Debug.Assert(numSegments >= 0);
|
||||
|
||||
var mesh = new Mesh();
|
||||
bool genCap = cap && radiusStart > 0f;
|
||||
|
||||
// We use the XY position of the vertices to compute the cone normal in the shader.
|
||||
// With a perfectly sharp cone, we couldn't compute accurate normals at its top.
|
||||
radiusStart = Mathf.Max(radiusStart, kMinTruncatedRadius);
|
||||
|
||||
int vertCountSides = numSides * (numSegments + 2);
|
||||
int vertCountTotal = vertCountSides;
|
||||
|
||||
if (genCap)
|
||||
vertCountTotal += numSides + 1;
|
||||
|
||||
// VERTICES
|
||||
{
|
||||
float angleOffset = GetAngleOffset(numSides);
|
||||
|
||||
var vertices = new Vector3[vertCountTotal];
|
||||
|
||||
for (int i = 0; i < numSides; i++)
|
||||
{
|
||||
float angle = angleOffset + 2 * Mathf.PI * i / numSides;
|
||||
float angleCos = Mathf.Cos(angle);
|
||||
float angleSin = Mathf.Sin(angle);
|
||||
|
||||
for (int seg = 0; seg < numSegments + 2; seg++)
|
||||
{
|
||||
float tseg = (float)seg / (numSegments + 1);
|
||||
Debug.Assert(tseg >= 0f && tseg <= 1f);
|
||||
float radius = Mathf.Lerp(radiusStart, radiusEnd, tseg);
|
||||
vertices[i + seg * numSides] = new Vector3(radius * angleCos, radius * angleSin, tseg * lengthZ);
|
||||
}
|
||||
}
|
||||
|
||||
if (genCap)
|
||||
{
|
||||
int ind = vertCountSides;
|
||||
|
||||
vertices[ind] = Vector3.zero;
|
||||
ind++;
|
||||
|
||||
for (int i = 0; i < numSides; i++)
|
||||
{
|
||||
float angle = angleOffset + 2 * Mathf.PI * i / numSides;
|
||||
float angleCos = Mathf.Cos(angle);
|
||||
float angleSin = Mathf.Sin(angle);
|
||||
vertices[ind] = new Vector3(radiusStart * angleCos, radiusStart * angleSin, 0f);
|
||||
ind++;
|
||||
}
|
||||
|
||||
Debug.Assert(ind == vertices.Length);
|
||||
}
|
||||
|
||||
if (!doubleSided)
|
||||
{
|
||||
mesh.vertices = vertices;
|
||||
}
|
||||
else
|
||||
{
|
||||
var vertices2 = new Vector3[vertices.Length * 2];
|
||||
vertices.CopyTo(vertices2, 0);
|
||||
vertices.CopyTo(vertices2, vertices.Length);
|
||||
mesh.vertices = vertices2;
|
||||
}
|
||||
}
|
||||
|
||||
// UV (used to flags vertices as sides or cap)
|
||||
// X: 0 = sides ; 1 = cap
|
||||
// Y: 0 = front face ; 1 = back face (doubleSided only)
|
||||
{
|
||||
var uv = new Vector2[vertCountTotal];
|
||||
int ind = 0;
|
||||
for (int i = 0; i < vertCountSides; i++)
|
||||
uv[ind++] = Vector2.zero;
|
||||
|
||||
if (genCap)
|
||||
{
|
||||
for (int i = 0; i < numSides + 1; i++)
|
||||
uv[ind++] = new Vector2(1, 0);
|
||||
}
|
||||
|
||||
Debug.Assert(ind == uv.Length);
|
||||
|
||||
|
||||
if (!doubleSided)
|
||||
{
|
||||
mesh.uv = uv;
|
||||
}
|
||||
else
|
||||
{
|
||||
var uv2 = new Vector2[uv.Length * 2];
|
||||
uv.CopyTo(uv2, 0);
|
||||
uv.CopyTo(uv2, uv.Length);
|
||||
|
||||
for (int i = 0; i < uv.Length; i++)
|
||||
{
|
||||
var value = uv2[i + uv.Length];
|
||||
uv2[i + uv.Length] = new Vector2(value.x, 1);
|
||||
}
|
||||
|
||||
mesh.uv = uv2;
|
||||
}
|
||||
}
|
||||
|
||||
// INDICES
|
||||
{
|
||||
int triCountSides = numSides * 2 * Mathf.Max(numSegments + 1, 1);
|
||||
int indCountSides = triCountSides * 3;
|
||||
int indCountTotal = indCountSides;
|
||||
|
||||
if (genCap)
|
||||
indCountTotal += numSides * 3;
|
||||
|
||||
var indices = new int[indCountTotal];
|
||||
int ind = 0;
|
||||
|
||||
for (int i = 0; i < numSides; i++)
|
||||
{
|
||||
int ip1 = i + 1;
|
||||
if (ip1 == numSides)
|
||||
ip1 = 0;
|
||||
|
||||
for (int k = 0; k < numSegments + 1; ++k)
|
||||
{
|
||||
var offset = k * numSides;
|
||||
|
||||
indices[ind++] = offset + i;
|
||||
indices[ind++] = offset + ip1;
|
||||
indices[ind++] = offset + i + numSides;
|
||||
|
||||
indices[ind++] = offset + ip1 + numSides;
|
||||
indices[ind++] = offset + i + numSides;
|
||||
indices[ind++] = offset + ip1;
|
||||
}
|
||||
}
|
||||
|
||||
if (genCap)
|
||||
{
|
||||
for (int i = 0; i < numSides - 1; i++)
|
||||
{
|
||||
indices[ind++] = vertCountSides;
|
||||
indices[ind++] = vertCountSides + i + 2;
|
||||
indices[ind++] = vertCountSides + i + 1;
|
||||
}
|
||||
|
||||
indices[ind++] = vertCountSides;
|
||||
indices[ind++] = vertCountSides + 1;
|
||||
indices[ind++] = vertCountSides + numSides;
|
||||
}
|
||||
|
||||
Debug.Assert(ind == indices.Length);
|
||||
|
||||
if (!doubleSided)
|
||||
{
|
||||
mesh.triangles = indices;
|
||||
}
|
||||
else
|
||||
{
|
||||
var indices2 = new int[indices.Length * 2];
|
||||
indices.CopyTo(indices2, 0);
|
||||
|
||||
for (int i = 0; i < indices.Length; i += 3)
|
||||
{
|
||||
indices2[indices.Length + i + 0] = indices[i + 0] + vertCountTotal;
|
||||
indices2[indices.Length + i + 1] = indices[i + 2] + vertCountTotal;
|
||||
indices2[indices.Length + i + 2] = indices[i + 1] + vertCountTotal;
|
||||
}
|
||||
|
||||
mesh.triangles = indices2;
|
||||
}
|
||||
}
|
||||
|
||||
mesh.bounds = ComputeBounds(lengthZ, radiusStart, radiusEnd);
|
||||
|
||||
Debug.Assert(mesh.vertexCount == GetVertexCount(numSides, numSegments, genCap, doubleSided));
|
||||
Debug.Assert(mesh.triangles.Length == GetIndicesCount(numSides, numSegments, genCap, doubleSided));
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
public static Bounds ComputeBounds(float lengthZ, float radiusStart, float radiusEnd)
|
||||
{
|
||||
float maxDiameter = Mathf.Max(radiusStart, radiusEnd) * 2;
|
||||
return new Bounds(
|
||||
new Vector3(0, 0, lengthZ * 0.5f),
|
||||
new Vector3(maxDiameter, maxDiameter, lengthZ)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public static int GetVertexCount(int numSides, int numSegments, bool geomCap, bool doubleSided)
|
||||
{
|
||||
Debug.Assert(numSides >= 2);
|
||||
Debug.Assert(numSegments >= 0);
|
||||
|
||||
int count = numSides * (numSegments + 2);
|
||||
if (geomCap) count += numSides + 1;
|
||||
if (doubleSided) count *= 2;
|
||||
return count;
|
||||
}
|
||||
|
||||
public static int GetIndicesCount(int numSides, int numSegments, bool geomCap, bool doubleSided)
|
||||
{
|
||||
Debug.Assert(numSides >= 2);
|
||||
Debug.Assert(numSegments >= 0);
|
||||
|
||||
int count = numSides * (numSegments + 1) * 2 * 3;
|
||||
if (geomCap) count += numSides * 3;
|
||||
if (doubleSided) count *= 2;
|
||||
return count;
|
||||
}
|
||||
|
||||
public static int GetSharedMeshVertexCount()
|
||||
{
|
||||
return GetVertexCount(Config.Instance.sharedMeshSides, Config.Instance.sharedMeshSegments, true, Config.Instance.requiresDoubleSidedMesh);
|
||||
}
|
||||
|
||||
public static int GetSharedMeshIndicesCount()
|
||||
{
|
||||
return GetIndicesCount(Config.Instance.sharedMeshSides, Config.Instance.sharedMeshSegments, true, Config.Instance.requiresDoubleSidedMesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bc0059e5d7876fb4b97e542dcc0e8350
|
||||
timeCreated: 1504627701
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
[Serializable]
|
||||
public struct MinMaxRangeFloat
|
||||
{
|
||||
public float minValue { get { return m_MinValue; } }
|
||||
public float maxValue { get { return m_MaxValue; } }
|
||||
|
||||
public float randomValue { get { return UnityEngine.Random.Range(minValue, maxValue); } }
|
||||
public Vector2 asVector2 { get { return new Vector2(minValue, maxValue); } }
|
||||
|
||||
public float GetLerpedValue(float lerp01) { return Mathf.Lerp(minValue, maxValue, lerp01); }
|
||||
|
||||
public MinMaxRangeFloat(float min, float max) { m_MinValue = min; m_MaxValue = max; Debug.Assert(min <= max); }
|
||||
|
||||
[SerializeField] float m_MinValue;
|
||||
[SerializeField] float m_MaxValue;
|
||||
}
|
||||
|
||||
public class MinMaxRangeAttribute : System.Attribute
|
||||
{
|
||||
public float minValue { get; private set; }
|
||||
public float maxValue { get; private set; }
|
||||
|
||||
public MinMaxRangeAttribute(float min, float max) { minValue = min; maxValue = max; Debug.Assert(min <= max); }
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[CustomPropertyDrawer(typeof(MinMaxRangeFloat), true)]
|
||||
public class MinMaxRangeFloatPropertyDrawer : PropertyDrawer
|
||||
{
|
||||
static float RoundFloat(float f) { return (float)Math.Round(f * 100f) / 100f; }
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
label = EditorGUI.BeginProperty(position, label, property);
|
||||
{
|
||||
position = EditorGUI.PrefixLabel(position, label);
|
||||
|
||||
var propMin = property.FindPropertyRelative("m_MinValue");
|
||||
Debug.AssertFormat(propMin != null, "Failed to find property 'MinMaxRangeFloat.m_MinValue'");
|
||||
var propMax = property.FindPropertyRelative("m_MaxValue");
|
||||
Debug.AssertFormat(propMax != null, "Failed to find property 'MinMaxRangeFloat.m_MaxValue'");
|
||||
|
||||
float valueMin = RoundFloat(propMin.floatValue);
|
||||
float valueMax = RoundFloat(propMax.floatValue);
|
||||
|
||||
float rangeMin = 0.0f, rangeMax = 1.0f;
|
||||
|
||||
var ranges = (MinMaxRangeAttribute[])fieldInfo.GetCustomAttributes(typeof(MinMaxRangeAttribute), true);
|
||||
if (ranges.Length > 0)
|
||||
{
|
||||
rangeMin = ranges[0].minValue;
|
||||
rangeMax = ranges[0].maxValue;
|
||||
}
|
||||
|
||||
const float kBoundsFieldWidth = 40.0f;
|
||||
const float kWidthOffset = 5.0f;
|
||||
|
||||
EditorGUI.showMixedValue = propMin.hasMultipleDifferentValues || propMax.hasMultipleDifferentValues;
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
{
|
||||
var rectMinValue = new Rect(position);
|
||||
rectMinValue.width = kBoundsFieldWidth;
|
||||
valueMin = EditorGUI.FloatField(rectMinValue, valueMin);
|
||||
|
||||
position.xMin += kBoundsFieldWidth + kWidthOffset;
|
||||
|
||||
var reactMaxValue = new Rect(position);
|
||||
reactMaxValue.xMin = reactMaxValue.xMax - kBoundsFieldWidth;
|
||||
valueMax = EditorGUI.FloatField(reactMaxValue, valueMax);
|
||||
|
||||
position.xMax -= kBoundsFieldWidth + kWidthOffset;
|
||||
|
||||
EditorGUI.MinMaxSlider(position, ref valueMin, ref valueMax, rangeMin, rangeMax);
|
||||
}
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
propMin.floatValue = valueMin;
|
||||
propMax.floatValue = valueMax;
|
||||
}
|
||||
}
|
||||
EditorGUI.showMixedValue = false;
|
||||
}
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dbd77861b0969d4469f82aed94865349
|
||||
timeCreated: 1617383958
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,77 @@
|
||||
using UnityEngine;
|
||||
|
||||
#pragma warning disable 0429, 0162 // Unreachable expression code detected (because of Noise3D.isSupported on mobile)
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
public static class Noise3D
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns if the 3D Noise feature is supported on the current platform or not.
|
||||
/// 3D Noise feature requires a graphicsShaderLevel 35 or higher (which is basically Shader Model 3.5 / OpenGL ES 3.0 or above)
|
||||
/// If not supported, the beams will look like the 3D Noise has been disabled.
|
||||
/// </summary>
|
||||
public static bool isSupported {
|
||||
get {
|
||||
if (!ms_IsSupportedChecked)
|
||||
{
|
||||
ms_IsSupported = SystemInfo.graphicsShaderLevel >= kMinShaderLevel;
|
||||
if (!ms_IsSupported)
|
||||
Debug.LogWarning(isNotSupportedString);
|
||||
ms_IsSupportedChecked = true;
|
||||
}
|
||||
return ms_IsSupported;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if the 3D Noise Texture has been successfully loaded or not.
|
||||
/// If the feature is not supported (isSupported == false), isProperlyLoaded is also false.
|
||||
/// </summary>
|
||||
public static bool isProperlyLoaded { get { return ms_NoiseTexture != null; } }
|
||||
|
||||
public static string isNotSupportedString { get {
|
||||
var str = string.Format("3D Noise requires higher shader capabilities (Shader Model 3.5 / OpenGL ES 3.0), which are not available on the current platform: graphicsShaderLevel (current/required) = {0} / {1}",
|
||||
SystemInfo.graphicsShaderLevel,
|
||||
kMinShaderLevel);
|
||||
#if UNITY_EDITOR
|
||||
str += "\nPlease change the editor's graphics emulation for a more capable one via \"Edit/Graphics Emulation\" and press Play to force the light beams to be recomputed.";
|
||||
#endif
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ms_IsSupportedChecked = false;
|
||||
static bool ms_IsSupported = false;
|
||||
static Texture3D ms_NoiseTexture = null;
|
||||
|
||||
const int kMinShaderLevel = 35; // Shader Model 3.5 / OpenGL ES 3.0 to handle sampler3D -> https://docs.unity3d.com/ScriptReference/SystemInfo-graphicsShaderLevel.html
|
||||
|
||||
[RuntimeInitializeOnLoadMethod]
|
||||
static void OnStartUp()
|
||||
{
|
||||
LoadIfNeeded();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public static void _EditorForceReloadData()
|
||||
{
|
||||
ms_NoiseTexture = null;
|
||||
LoadIfNeeded();
|
||||
}
|
||||
#endif
|
||||
|
||||
public static void LoadIfNeeded()
|
||||
{
|
||||
if (!isSupported) return;
|
||||
|
||||
if (ms_NoiseTexture == null)
|
||||
{
|
||||
ms_NoiseTexture = Config.Instance.noiseTexture3D;
|
||||
|
||||
Shader.SetGlobalTexture(ShaderProperties.GlobalNoiseTex3D, ms_NoiseTexture);
|
||||
Shader.SetGlobalFloat(ShaderProperties.GlobalNoiseCustomTime, -1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57abb2ea789b7604eae964ef5c2fdcbb
|
||||
timeCreated: 1508397352
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,107 @@
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
public class PlatformHelper
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
static BuildTarget ms_BuildTargetOverride = BuildTarget.NoTarget;
|
||||
|
||||
public static void SetBuildTargetOverride(BuildTarget target)
|
||||
{
|
||||
ms_BuildTargetOverride = target;
|
||||
Config.Instance.RefreshShader(Config.RefreshShaderFlags.All);
|
||||
}
|
||||
|
||||
static BuildTarget GetCurrentBuildTarget()
|
||||
{
|
||||
if (BuildPipeline.isBuildingPlayer && ms_BuildTargetOverride != BuildTarget.NoTarget) return ms_BuildTargetOverride;
|
||||
return EditorUserBuildSettings.activeBuildTarget;
|
||||
}
|
||||
|
||||
static RuntimePlatform BuildTargetToRuntimePlatform(BuildTarget buildTarget)
|
||||
{
|
||||
#pragma warning disable 0618 // obsolete BuildTargets
|
||||
switch (buildTarget)
|
||||
{
|
||||
case BuildTarget.Android: return RuntimePlatform.Android;
|
||||
case BuildTarget.PS4: return RuntimePlatform.PS4;
|
||||
case BuildTarget.StandaloneLinux64: return RuntimePlatform.LinuxPlayer;
|
||||
case BuildTarget.StandaloneWindows: return RuntimePlatform.WindowsPlayer;
|
||||
case BuildTarget.StandaloneWindows64: return RuntimePlatform.WindowsPlayer;
|
||||
case BuildTarget.WSAPlayer: return RuntimePlatform.WSAPlayerARM;
|
||||
case BuildTarget.XboxOne: return RuntimePlatform.XboxOne;
|
||||
case BuildTarget.iOS: return RuntimePlatform.IPhonePlayer;
|
||||
case BuildTarget.tvOS: return RuntimePlatform.tvOS;
|
||||
case BuildTarget.WebGL: return RuntimePlatform.WebGLPlayer;
|
||||
|
||||
#if UNITY_2017_1_OR_NEWER
|
||||
case BuildTarget.Switch: return RuntimePlatform.Switch;
|
||||
#endif
|
||||
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
case BuildTarget.StandaloneOSX: return RuntimePlatform.OSXPlayer;
|
||||
#else
|
||||
case BuildTarget.StandaloneOSXUniversal:return RuntimePlatform.OSXPlayer;
|
||||
#endif
|
||||
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
case BuildTarget.Stadia: return RuntimePlatform.Stadia;
|
||||
#endif
|
||||
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
case BuildTarget.CloudRendering: return RuntimePlatform.CloudRendering;
|
||||
#endif
|
||||
|
||||
#if UNITY_2021_1_OR_NEWER
|
||||
case BuildTarget.GameCoreScarlett: return RuntimePlatform.GameCoreScarlett;
|
||||
case BuildTarget.GameCoreXboxOne: return RuntimePlatform.GameCoreXboxOne;
|
||||
case BuildTarget.PS5: return RuntimePlatform.PS5;
|
||||
#endif
|
||||
|
||||
// obsolete
|
||||
case BuildTarget.StandaloneOSXIntel: return RuntimePlatform.OSXPlayer;
|
||||
case BuildTarget.StandaloneOSXIntel64: return RuntimePlatform.OSXPlayer;
|
||||
case BuildTarget.StandaloneLinuxUniversal:return RuntimePlatform.LinuxPlayer;
|
||||
case BuildTarget.WiiU: return RuntimePlatform.WiiU;
|
||||
case BuildTarget.PSP2: return RuntimePlatform.PSP2;
|
||||
case BuildTarget.PS3: return RuntimePlatform.PS3;
|
||||
case BuildTarget.XBOX360: return RuntimePlatform.XBOX360;
|
||||
|
||||
default: return (RuntimePlatform)(-1);
|
||||
}
|
||||
#pragma warning restore 0618
|
||||
}
|
||||
|
||||
public static bool IsValidPlatformSuffix(string suffix)
|
||||
{
|
||||
if (string.IsNullOrEmpty(suffix))
|
||||
return true;
|
||||
|
||||
foreach (var platform in System.Enum.GetNames(typeof(RuntimePlatform)))
|
||||
{
|
||||
if (suffix == platform)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif // UNITY_EDITOR
|
||||
|
||||
public static string GetCurrentPlatformSuffix()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
return GetPlatformSuffix(BuildTargetToRuntimePlatform(GetCurrentBuildTarget()));
|
||||
#else
|
||||
return GetPlatformSuffix(Application.platform);
|
||||
#endif
|
||||
}
|
||||
|
||||
static string GetPlatformSuffix(RuntimePlatform platform)
|
||||
{
|
||||
return platform.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30abcef3cbaaee1408d5966265dc731b
|
||||
timeCreated: 1612121684
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,84 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
public class PolygonHelper : MonoBehaviour
|
||||
{
|
||||
public struct Plane2D
|
||||
{
|
||||
public Vector2 normal;
|
||||
public float distance;
|
||||
|
||||
public float Distance(Vector2 point) { return Vector2.Dot(normal, point) + distance; }
|
||||
|
||||
public Vector2 ClosestPoint(Vector2 pt) { return pt - normal * Distance(pt); }
|
||||
|
||||
public Vector2 Intersect(Vector2 p1, Vector2 p2)
|
||||
{
|
||||
float denominator = Vector2.Dot(normal, p1 - p2);
|
||||
|
||||
if (Utils.IsAlmostZero(denominator))
|
||||
return (p1 + p2) * 0.5f;
|
||||
|
||||
float u = (normal.x * p1.x + normal.y * p1.y + distance) / denominator;
|
||||
return (p1 + u * (p2 - p1));
|
||||
}
|
||||
|
||||
public bool GetSide(Vector2 point) { return Distance(point) > 0.0f; }
|
||||
|
||||
public static Plane2D FromPoints(Vector3 p1, Vector3 p2)
|
||||
{
|
||||
var v = (p2 - p1).normalized;
|
||||
|
||||
return new Plane2D
|
||||
{
|
||||
normal = new Vector2(v.y, -v.x),
|
||||
distance = (-v.y * p1.x + v.x * p1.y)
|
||||
};
|
||||
}
|
||||
|
||||
public static Plane2D FromNormalAndPoint(Vector3 normalizedNormal, Vector3 p1)
|
||||
{
|
||||
return new Plane2D
|
||||
{
|
||||
normal = normalizedNormal,
|
||||
distance = (-normalizedNormal.x * p1.x - normalizedNormal.y * p1.y)
|
||||
};
|
||||
}
|
||||
|
||||
public void Flip() { normal = -normal; distance = -distance; }
|
||||
|
||||
public Vector2[] CutConvex(Vector2[] poly)
|
||||
{
|
||||
Debug.Assert(poly.Length >= 3);
|
||||
var polyOut = new List<Vector2>(poly.Length);
|
||||
|
||||
Vector2 startingPoint = poly[poly.Length - 1];
|
||||
foreach (var endPoint in poly)
|
||||
{
|
||||
var startingSide = GetSide(startingPoint);
|
||||
var endSide = GetSide(endPoint);
|
||||
if (startingSide && endSide)
|
||||
{
|
||||
polyOut.Add(endPoint);
|
||||
}
|
||||
else if (startingSide && !endSide)
|
||||
{
|
||||
polyOut.Add(Intersect(startingPoint, endPoint));
|
||||
}
|
||||
else if (!startingSide && endSide)
|
||||
{
|
||||
polyOut.Add(Intersect(startingPoint, endPoint));
|
||||
polyOut.Add(endPoint);
|
||||
}
|
||||
startingPoint = endPoint;
|
||||
}
|
||||
|
||||
return polyOut.ToArray();
|
||||
}
|
||||
|
||||
public override string ToString() { return string.Format("{0} x {1} + {2}", normal.x, normal.y, distance); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b55955c734a6a04db43faaa6390235a
|
||||
timeCreated: 1596631375
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,86 @@
|
||||
#if UNITY_2018_1_OR_NEWER
|
||||
#define VLB_SRP_SUPPORT // Comment this to disable SRP support
|
||||
#endif
|
||||
|
||||
#if VLB_SRP_SUPPORT
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
using AliasCurrentPipeline = UnityEngine.Rendering.RenderPipelineManager;
|
||||
using AliasCameraEvents = UnityEngine.Rendering.RenderPipelineManager;
|
||||
using CallbackType = System.Action<UnityEngine.Rendering.ScriptableRenderContext, UnityEngine.Camera>;
|
||||
#else
|
||||
using AliasCurrentPipeline = UnityEngine.Experimental.Rendering.RenderPipelineManager;
|
||||
using AliasCameraEvents = UnityEngine.Experimental.Rendering.RenderPipeline;
|
||||
using CallbackType = System.Action<UnityEngine.Camera>;
|
||||
#endif // UNITY_2019_1_OR_NEWER
|
||||
#endif // VLB_SRP_SUPPORT
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
public static class SRPHelper
|
||||
{
|
||||
public enum RenderPipeline
|
||||
{
|
||||
Undefined,
|
||||
BuiltIn,
|
||||
URP,
|
||||
LWRP,
|
||||
HDRP,
|
||||
}
|
||||
|
||||
public static RenderPipeline renderPipelineType
|
||||
{
|
||||
get
|
||||
{
|
||||
// cache the value to prevent from comparing strings (in ComputeRenderPipeline) each frame when SRPBatcher is enabled
|
||||
if (m_RenderPipelineCached == RenderPipeline.Undefined)
|
||||
m_RenderPipelineCached = ComputeRenderPipeline();
|
||||
return m_RenderPipelineCached;
|
||||
}
|
||||
}
|
||||
|
||||
static RenderPipeline m_RenderPipelineCached = RenderPipeline.Undefined;
|
||||
|
||||
static RenderPipeline ComputeRenderPipeline()
|
||||
{
|
||||
#if VLB_SRP_SUPPORT
|
||||
var rp = UnityEngine.Rendering.GraphicsSettings.defaultRenderPipeline;
|
||||
if (rp)
|
||||
{
|
||||
var name = rp.GetType().ToString();
|
||||
if (name.Contains("Universal")) return RenderPipeline.URP;
|
||||
if (name.Contains("Lightweight")) return RenderPipeline.LWRP;
|
||||
if (name.Contains("HD")) return RenderPipeline.HDRP;
|
||||
}
|
||||
#endif
|
||||
return RenderPipeline.BuiltIn;
|
||||
}
|
||||
|
||||
#if VLB_SRP_SUPPORT
|
||||
public static bool IsUsingCustomRenderPipeline()
|
||||
{
|
||||
// TODO: optimize and use renderPipelineType
|
||||
return AliasCurrentPipeline.currentPipeline != null || UnityEngine.Rendering.GraphicsSettings.defaultRenderPipeline != null;
|
||||
}
|
||||
|
||||
public static void RegisterOnBeginCameraRendering(CallbackType cb)
|
||||
{
|
||||
if (IsUsingCustomRenderPipeline())
|
||||
{
|
||||
AliasCameraEvents.beginCameraRendering -= cb;
|
||||
AliasCameraEvents.beginCameraRendering += cb;
|
||||
}
|
||||
}
|
||||
|
||||
public static void UnregisterOnBeginCameraRendering(CallbackType cb)
|
||||
{
|
||||
if (IsUsingCustomRenderPipeline())
|
||||
{
|
||||
AliasCameraEvents.beginCameraRendering -= cb;
|
||||
}
|
||||
}
|
||||
#else
|
||||
public static bool IsUsingCustomRenderPipeline() { return false; }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8086b200893fe0c4e88663a09fdc78a3
|
||||
timeCreated: 1585382401
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,318 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
public class ShaderGenerator : ScriptableObject
|
||||
{
|
||||
[SerializeField] TextAsset m_Base = null;
|
||||
TextAsset textAssetBase { get { return m_Base; } }
|
||||
|
||||
[SerializeField] TextAsset m_Pass = null;
|
||||
TextAsset textAssetPass { get { return m_Pass; } }
|
||||
|
||||
[SerializeField] TextAsset m_IncludesBuiltin = null;
|
||||
TextAsset textAssetIncludesBuiltin { get { return m_IncludesBuiltin; } }
|
||||
|
||||
[SerializeField] TextAsset m_IncludesURP = null;
|
||||
TextAsset textAssetIncludesURP { get { return m_IncludesURP; } }
|
||||
|
||||
[SerializeField] TextAsset m_IncludesHDRP = null;
|
||||
TextAsset textAssetIncludesHDRP { get { return m_IncludesHDRP; } }
|
||||
|
||||
const string kShaderAssetName = "VLBGeneratedShader";
|
||||
|
||||
static string GetOutputPath()
|
||||
{
|
||||
var path = Path.GetDirectoryName(AssetDatabase.GetAssetPath(Instance));
|
||||
if (string.IsNullOrEmpty(path)) path = Consts.PluginFolder + "/Shaders/";
|
||||
path = path.Replace("\\", "/");
|
||||
const string kAssetsFolder = "Assets/";
|
||||
if (path.IndexOf(kAssetsFolder) == 0) path = path.Remove(0, kAssetsFolder.Length);
|
||||
return path;
|
||||
}
|
||||
|
||||
public class EnabledFeatures
|
||||
{
|
||||
public bool dithering;
|
||||
public bool depthBlend;
|
||||
public FeatureEnabledColorGradient colorGradient;
|
||||
public bool noise3D;
|
||||
public bool dynamicOcclusion;
|
||||
public bool meshSkewing;
|
||||
public bool shaderAccuracyHigh;
|
||||
}
|
||||
|
||||
public static Shader Generate(RenderPipeline rp, RenderingMode rm, EnabledFeatures enabledFeatures)
|
||||
{
|
||||
// The instance might not be accessible yet, when called from Config.OnEnable for instance if the Config is enabled before the ShaderGenerator.
|
||||
// In this case we don't generate the shader right away, we store the parameters instead and we'll generate the shader in ShaderGenerator.OnEnable.
|
||||
if (Instance == null)
|
||||
{
|
||||
ms_GenerationParamOnEnable = new GenerationParam { renderPipeline = rp, renderingMode = rm, enabledFeatures = enabledFeatures };
|
||||
return null;
|
||||
}
|
||||
|
||||
return new GenShader(rp, rm, enabledFeatures).Generate();
|
||||
}
|
||||
|
||||
static string LoadText(TextAsset textAsset)
|
||||
{
|
||||
Debug.Assert(textAsset != null, "Fail to load a TextAsset, please try to reinstall the VolumetricLightBeam plugin");
|
||||
return textAsset.text;
|
||||
}
|
||||
|
||||
TextAsset GetTextAssetIncludes(SRPHelper.RenderPipeline rp)
|
||||
{
|
||||
switch (rp)
|
||||
{
|
||||
case SRPHelper.RenderPipeline.BuiltIn:
|
||||
return textAssetIncludesBuiltin;
|
||||
case SRPHelper.RenderPipeline.LWRP:
|
||||
case SRPHelper.RenderPipeline.URP:
|
||||
return textAssetIncludesURP;
|
||||
case SRPHelper.RenderPipeline.HDRP:
|
||||
return textAssetIncludesHDRP;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static bool IsFogSupported(SRPHelper.RenderPipeline rp) { return rp != SRPHelper.RenderPipeline.HDRP; }
|
||||
|
||||
enum ShaderLangage { CG, HLSL }
|
||||
|
||||
static ShaderLangage GetShaderLangage(SRPHelper.RenderPipeline rp)
|
||||
{
|
||||
switch (rp)
|
||||
{
|
||||
case SRPHelper.RenderPipeline.BuiltIn:
|
||||
case SRPHelper.RenderPipeline.LWRP:
|
||||
return ShaderLangage.CG;
|
||||
case SRPHelper.RenderPipeline.URP:
|
||||
case SRPHelper.RenderPipeline.HDRP:
|
||||
return ShaderLangage.HLSL;
|
||||
}
|
||||
return ShaderLangage.CG;
|
||||
}
|
||||
|
||||
static string GetShaderLangagePre (ShaderLangage lang) { return lang == ShaderLangage.CG ? "CGPROGRAM" : "HLSLPROGRAM"; }
|
||||
static string GetShaderLangagePost(ShaderLangage lang) { return lang == ShaderLangage.CG ? "ENDCG" : "ENDHLSL"; }
|
||||
|
||||
public class GenPass
|
||||
{
|
||||
CullMode m_CullMode;
|
||||
|
||||
public GenPass(CullMode cullMode)
|
||||
{
|
||||
m_CullMode = cullMode;
|
||||
}
|
||||
|
||||
static void AppendMultiCompile(ref string str, bool genDefaultVariant, params string[] options)
|
||||
{
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
const string kPrefix = " #pragma multi_compile_local";
|
||||
#else
|
||||
const string kPrefix = " #pragma multi_compile";
|
||||
#endif
|
||||
str += kPrefix;
|
||||
if(genDefaultVariant) str += " __";
|
||||
foreach (string opt in options) str += " " + opt;
|
||||
str += System.Environment.NewLine;
|
||||
}
|
||||
|
||||
public string Generate(SRPHelper.RenderPipeline rp, RenderingMode rm, EnabledFeatures enabledFeatures, int passID, int passCount)
|
||||
{
|
||||
var code = LoadText(Instance.textAssetPass);
|
||||
|
||||
code = code.Replace("{VLB_GEN_CULLING}", m_CullMode.ToString());
|
||||
code = code.Replace("{VLB_GEN_PRAGMA_INSTANCING}", rm == RenderingMode.GPUInstancing ? "#pragma multi_compile_instancing" : "");
|
||||
code = code.Replace("{VLB_GEN_PRAGMA_FOG}", IsFogSupported(rp) ? "#pragma multi_compile_fog" : "");
|
||||
|
||||
string multiCompileVariants = "";
|
||||
AppendMultiCompile(ref multiCompileVariants, true, ShaderKeywords.AlphaAsBlack);
|
||||
if (enabledFeatures.noise3D) AppendMultiCompile(ref multiCompileVariants, true, ShaderKeywords.Noise3D);
|
||||
if (enabledFeatures.depthBlend) AppendMultiCompile(ref multiCompileVariants, true, ShaderKeywords.DepthBlend);
|
||||
switch(enabledFeatures.colorGradient)
|
||||
{
|
||||
case FeatureEnabledColorGradient.HighOnly: AppendMultiCompile(ref multiCompileVariants, true, ShaderKeywords.ColorGradientMatrixHigh); break;
|
||||
case FeatureEnabledColorGradient.HighAndLow: AppendMultiCompile(ref multiCompileVariants, true, ShaderKeywords.ColorGradientMatrixHigh, ShaderKeywords.ColorGradientMatrixLow); break;
|
||||
}
|
||||
if (enabledFeatures.dynamicOcclusion) AppendMultiCompile(ref multiCompileVariants, true, ShaderKeywords.OcclusionClippingPlane, ShaderKeywords.OcclusionDepthTexture);
|
||||
if (enabledFeatures.meshSkewing) AppendMultiCompile(ref multiCompileVariants, true, ShaderKeywords.MeshSkewing);
|
||||
if (enabledFeatures.shaderAccuracyHigh) AppendMultiCompile(ref multiCompileVariants, true, ShaderKeywords.ShaderAccuracyHigh);
|
||||
code = code.Replace("{VLB_GEN_PRAGMA_MULTI_COMPILE_VARIANTS}", multiCompileVariants);
|
||||
|
||||
var lang = GetShaderLangage(rp);
|
||||
code = code.Replace("{VLB_GEN_PROGRAM_PRE}", GetShaderLangagePre(lang));
|
||||
code = code.Replace("{VLB_GEN_PROGRAM_POST}", GetShaderLangagePost(lang));
|
||||
|
||||
var passPre = "";
|
||||
|
||||
if (passCount > 1)
|
||||
{
|
||||
code = code.Replace("{VLB_GEN_INPUT_VS}", string.Format("{0}", passID));
|
||||
code = code.Replace("{VLB_GEN_INPUT_FS}", string.Format("{0}", passID));
|
||||
}
|
||||
else
|
||||
{
|
||||
code = code.Replace("{VLB_GEN_INPUT_VS}", string.Format("{0}", "v.texcoord.y"));
|
||||
code = code.Replace("{VLB_GEN_INPUT_FS}", string.Format("{0}", "i.cameraPosObjectSpace_outsideBeam.w"));
|
||||
passPre += " #define VLB_PASS_OUTSIDEBEAM_FROM_VS_TO_FS 1" + System.Environment.NewLine;
|
||||
}
|
||||
|
||||
if (rp != SRPHelper.RenderPipeline.BuiltIn)
|
||||
{
|
||||
passPre += " #define VLB_SRP_API 1" + System.Environment.NewLine;
|
||||
|
||||
if (rm == RenderingMode.SRPBatcher)
|
||||
{
|
||||
passPre += " #define VLB_SRP_BATCHER 1" + System.Environment.NewLine;
|
||||
|
||||
if (rp == SRPHelper.RenderPipeline.URP)
|
||||
{
|
||||
// force enable constant buffers to fix SRP Batcher support on Android
|
||||
passPre += " #pragma enable_cbuffer" + System.Environment.NewLine;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (enabledFeatures.dithering)
|
||||
{
|
||||
passPre += " #define VLB_DITHERING 1" + System.Environment.NewLine;
|
||||
}
|
||||
|
||||
passPre += LoadText(Instance.GetTextAssetIncludes(rp));
|
||||
|
||||
code = code.Replace("{VLB_GEN_PRE}", passPre);
|
||||
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
class GenShader
|
||||
{
|
||||
SRPHelper.RenderPipeline m_RenderPipeline;
|
||||
RenderingMode m_RenderingMode;
|
||||
EnabledFeatures m_EnabledFeatures;
|
||||
List<GenPass> m_Passes = new List<GenPass>();
|
||||
|
||||
public GenShader(RenderPipeline rp, RenderingMode rm, EnabledFeatures enabledFeatures)
|
||||
{
|
||||
m_RenderingMode = rm;
|
||||
m_EnabledFeatures = enabledFeatures;
|
||||
|
||||
switch (rp)
|
||||
{
|
||||
case RenderPipeline.BuiltIn:
|
||||
AddPass(CullMode.Front);
|
||||
if (rm == RenderingMode.MultiPass) AddPass(CullMode.Back);
|
||||
m_RenderPipeline = SRPHelper.RenderPipeline.BuiltIn;
|
||||
break;
|
||||
|
||||
case RenderPipeline.URP:
|
||||
AddPass(CullMode.Front);
|
||||
m_RenderPipeline = SRPHelper.RenderPipeline.URP;
|
||||
break;
|
||||
|
||||
case RenderPipeline.HDRP:
|
||||
AddPass(CullMode.Front);
|
||||
m_RenderPipeline = SRPHelper.RenderPipeline.HDRP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GenShader AddPass(CullMode cullMode)
|
||||
{
|
||||
m_Passes.Add(new GenPass(cullMode));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Shader Generate()
|
||||
{
|
||||
var shaderName = string.Format("Hidden/VLB_{0}_{1}", m_RenderPipeline, m_RenderingMode);
|
||||
var code = LoadText(Instance.textAssetBase);
|
||||
code = code.Replace("{VLB_GEN_SHADERNAME}", shaderName);
|
||||
|
||||
var passes = "";
|
||||
for (int i = 0; i < m_Passes.Count; ++i)
|
||||
passes += m_Passes[i].Generate(m_RenderPipeline, m_RenderingMode, m_EnabledFeatures, i, m_Passes.Count);
|
||||
|
||||
code = code.Replace("{VLB_GEN_PASSES}", passes);
|
||||
code = code.Replace("{VLB_GEN_SPECIFIC_INCLUDE}", GetRenderPipelineInclude(m_RenderPipeline));
|
||||
|
||||
var outputFolderPath = ShaderGenerator.GetOutputPath();
|
||||
var outputFilePath = Path.Combine(outputFolderPath, kShaderAssetName);
|
||||
var outputFullPath = System.IO.Path.Combine(Application.dataPath, outputFilePath + ".shader");
|
||||
File.WriteAllText(outputFullPath, code);
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
var shader = Shader.Find(shaderName);
|
||||
Debug.Assert(shader != null, string.Format("Failed to generate shader '{0}' at '{1}'", shaderName, outputFullPath));
|
||||
return shader;
|
||||
}
|
||||
|
||||
string GetRenderPipelineInclude(SRPHelper.RenderPipeline rp)
|
||||
{
|
||||
switch(rp)
|
||||
{
|
||||
case SRPHelper.RenderPipeline.BuiltIn: return "ShaderSpecificBuiltin.cginc";
|
||||
case SRPHelper.RenderPipeline.HDRP: return "ShaderSpecificHDRP.hlsl";
|
||||
case SRPHelper.RenderPipeline.LWRP:
|
||||
case SRPHelper.RenderPipeline.URP: return "ShaderSpecificURP.cginc";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static ShaderGenerator FindInstance()
|
||||
{
|
||||
var assetGUIDs = AssetDatabase.FindAssets("ShaderGenerator");
|
||||
|
||||
foreach (var guid in assetGUIDs)
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var asset = AssetDatabase.LoadAssetAtPath<ShaderGenerator>(path);
|
||||
if (asset)
|
||||
return asset;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Store data to generate the shader on OnEnable
|
||||
class GenerationParam
|
||||
{
|
||||
public RenderPipeline renderPipeline;
|
||||
public RenderingMode renderingMode;
|
||||
public EnabledFeatures enabledFeatures;
|
||||
}
|
||||
|
||||
static GenerationParam ms_GenerationParamOnEnable = null;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if(ms_GenerationParamOnEnable != null && Instance != null)
|
||||
{
|
||||
Generate(ms_GenerationParamOnEnable.renderPipeline, ms_GenerationParamOnEnable.renderingMode, ms_GenerationParamOnEnable.enabledFeatures);
|
||||
ms_GenerationParamOnEnable = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton management
|
||||
static ShaderGenerator m_Instance = null;
|
||||
static ShaderGenerator Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Instance == null)
|
||||
m_Instance = FindInstance();
|
||||
return m_Instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a0074f2031380c488da5e38151a7a47
|
||||
timeCreated: 1592812245
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace VLB
|
||||
{
|
||||
public static class ShaderKeywords
|
||||
{
|
||||
public const string AlphaAsBlack = "VLB_ALPHA_AS_BLACK";
|
||||
public const string ColorGradientMatrixLow = "VLB_COLOR_GRADIENT_MATRIX_LOW";
|
||||
public const string ColorGradientMatrixHigh = "VLB_COLOR_GRADIENT_MATRIX_HIGH";
|
||||
public const string DepthBlend = "VLB_DEPTH_BLEND";
|
||||
public const string Noise3D = "VLB_NOISE_3D";
|
||||
public const string OcclusionClippingPlane = "VLB_OCCLUSION_CLIPPING_PLANE";
|
||||
public const string OcclusionDepthTexture = "VLB_OCCLUSION_DEPTH_TEXTURE";
|
||||
public const string MeshSkewing = "VLB_MESH_SKEWING";
|
||||
public const string ShaderAccuracyHigh = "VLB_SHADER_ACCURACY_HIGH";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d03404178af782e419e82d8a3b973df5
|
||||
timeCreated: 1607620118
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,47 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
public static class ShaderProperties
|
||||
{
|
||||
public static readonly int FadeOutFactor = Shader.PropertyToID("_FadeOutFactor");
|
||||
public static readonly int ConeSlopeCosSin = Shader.PropertyToID("_ConeSlopeCosSin");
|
||||
public static readonly int ConeRadius = Shader.PropertyToID("_ConeRadius");
|
||||
public static readonly int ConeApexOffsetZ = Shader.PropertyToID("_ConeApexOffsetZ");
|
||||
public static readonly int ColorFlat = Shader.PropertyToID("_ColorFlat");
|
||||
public static readonly int AlphaInside = Shader.PropertyToID("_AlphaInside");
|
||||
public static readonly int AlphaOutside = Shader.PropertyToID("_AlphaOutside");
|
||||
public static readonly int AttenuationLerpLinearQuad = Shader.PropertyToID("_AttenuationLerpLinearQuad");
|
||||
public static readonly int DistanceFallOff = Shader.PropertyToID("_DistanceFallOff");
|
||||
public static readonly int DistanceCamClipping = Shader.PropertyToID("_DistanceCamClipping");
|
||||
public static readonly int FresnelPow = Shader.PropertyToID("_FresnelPow");
|
||||
public static readonly int GlareBehind = Shader.PropertyToID("_GlareBehind");
|
||||
public static readonly int GlareFrontal = Shader.PropertyToID("_GlareFrontal");
|
||||
public static readonly int DrawCap = Shader.PropertyToID("_DrawCap");
|
||||
public static readonly int DepthBlendDistance = Shader.PropertyToID("_DepthBlendDistance");
|
||||
public static readonly int NoiseVelocityAndScale = Shader.PropertyToID("_NoiseVelocityAndScale");
|
||||
public static readonly int NoiseParam = Shader.PropertyToID("_NoiseParam");
|
||||
public static readonly int CameraParams = Shader.PropertyToID("_CameraParams");
|
||||
public static readonly int ColorGradientMatrix = Shader.PropertyToID("_ColorGradientMatrix");
|
||||
public static readonly int LocalToWorldMatrix = Shader.PropertyToID("_LocalToWorldMatrix");
|
||||
public static readonly int WorldToLocalMatrix = Shader.PropertyToID("_WorldToLocalMatrix");
|
||||
public static readonly int BlendSrcFactor = Shader.PropertyToID("_BlendSrcFactor");
|
||||
public static readonly int BlendDstFactor = Shader.PropertyToID("_BlendDstFactor");
|
||||
public static readonly int DynamicOcclusionClippingPlaneWS = Shader.PropertyToID("_DynamicOcclusionClippingPlaneWS");
|
||||
public static readonly int DynamicOcclusionClippingPlaneProps = Shader.PropertyToID("_DynamicOcclusionClippingPlaneProps");
|
||||
public static readonly int DynamicOcclusionDepthTexture = Shader.PropertyToID("_DynamicOcclusionDepthTexture");
|
||||
public static readonly int DynamicOcclusionDepthProps = Shader.PropertyToID("_DynamicOcclusionDepthProps");
|
||||
public static readonly int LocalForwardDirection = Shader.PropertyToID("_LocalForwardDirection");
|
||||
public static readonly int TiltVector = Shader.PropertyToID("_TiltVector");
|
||||
public static readonly int AdditionalClippingPlaneWS = Shader.PropertyToID("_AdditionalClippingPlaneWS");
|
||||
|
||||
public static readonly int ParticlesTintColor = Shader.PropertyToID("_TintColor");
|
||||
|
||||
public static readonly int GlobalUsesReversedZBuffer = Shader.PropertyToID("_VLB_UsesReversedZBuffer");
|
||||
public static readonly int GlobalNoiseTex3D = Shader.PropertyToID("_VLB_NoiseTex3D");
|
||||
public static readonly int GlobalNoiseCustomTime = Shader.PropertyToID("_VLB_NoiseCustomTime");
|
||||
public static readonly int GlobalDitheringFactor = Shader.PropertyToID("_VLB_DitheringFactor");
|
||||
public static readonly int GlobalDitheringNoiseTex = Shader.PropertyToID("_VLB_DitheringNoiseTex");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7c111997c90d464c94acc87b1711d59
|
||||
timeCreated: 1560540688
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,57 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
[HelpURL(Consts.Help.UrlSkewingHandle)]
|
||||
public class SkewingHandle : MonoBehaviour
|
||||
{
|
||||
public VolumetricLightBeam 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: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 96c382e4368041b4aad93aed0a833e2b, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,29 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
public static class TransformUtils
|
||||
{
|
||||
public struct Packed
|
||||
{
|
||||
public Vector3 position;
|
||||
public Quaternion rotation;
|
||||
public Vector3 lossyScale;
|
||||
|
||||
public bool IsSame(Transform transf)
|
||||
{
|
||||
return transf.position == position && transf.rotation == rotation && transf.lossyScale == lossyScale;
|
||||
}
|
||||
}
|
||||
|
||||
public static Packed GetWorldPacked(this Transform self)
|
||||
{
|
||||
return new Packed()
|
||||
{
|
||||
position = self.position,
|
||||
rotation = self.rotation,
|
||||
lossyScale = self.lossyScale,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db47de7f89a659b49982236fc59b4e04
|
||||
timeCreated: 1578089892
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,153 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
[RequireComponent(typeof(VolumetricLightBeam))]
|
||||
[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(m_Beam.dimensions == 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;
|
||||
|
||||
VolumetricLightBeam m_Beam = null;
|
||||
DynamicOcclusionRaycasting m_DynamicOcclusionRaycasting = null;
|
||||
PolygonCollider2D m_PolygonCollider2D = null;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
m_Beam = GetComponent<VolumetricLightBeam>();
|
||||
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 rangeEnd = m_Beam.fallOffEnd * rangeMultiplier;
|
||||
var lerpedRadiusEnd = Mathf.LerpUnclamped(m_Beam.coneRadiusStart, m_Beam.coneRadiusEnd, rangeMultiplier);
|
||||
|
||||
if (m_Beam.dimensions == Dimensions.Dim3D)
|
||||
{
|
||||
var meshCollider = gameObject.GetOrAddComponent<MeshCollider>();
|
||||
Debug.Assert(meshCollider);
|
||||
|
||||
int sides = Mathf.Min(m_Beam.geomSides, kMeshColliderNumSides);
|
||||
var mesh = MeshGenerator.GenerateConeZ_Radius(rangeEnd, m_Beam.coneRadiusStart, lerpedRadiusEnd, sides, 0, false, 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, -m_Beam.coneRadiusStart),
|
||||
new Vector2(rangeEnd, -lerpedRadiusEnd),
|
||||
new Vector2(rangeEnd, lerpedRadiusEnd),
|
||||
new Vector2(0.0f, m_Beam.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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b5217c9f8723a447824a7530d14c03d
|
||||
timeCreated: 1531459125
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 568401c2857b94943af27dc14afca0ae, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,249 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
public static class Utils
|
||||
{
|
||||
public static float ComputeConeRadiusEnd(float fallOffEnd, float spotAngle)
|
||||
{
|
||||
return fallOffEnd * Mathf.Tan(spotAngle * Mathf.Deg2Rad * 0.5f);
|
||||
}
|
||||
|
||||
public static float ComputeSpotAngle(float fallOffEnd, float coneRadiusEnd)
|
||||
{
|
||||
return Mathf.Atan2(coneRadiusEnd, fallOffEnd) * Mathf.Rad2Deg * 2.0f;
|
||||
}
|
||||
|
||||
public static void Swap<T>(ref T a, ref T b)
|
||||
{
|
||||
var temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
}
|
||||
|
||||
public static string GetPath(Transform current)
|
||||
{
|
||||
if (current.parent == null)
|
||||
return "/" + current.name;
|
||||
return GetPath(current.parent) + "/" + current.name;
|
||||
}
|
||||
|
||||
public static T NewWithComponent<T>(string name) where T : Component
|
||||
{
|
||||
return (new GameObject(name, typeof(T))).GetComponent<T>();
|
||||
}
|
||||
|
||||
public static T GetOrAddComponent<T>(this GameObject self) where T : Component
|
||||
{
|
||||
var component = self.GetComponent<T>();
|
||||
if (component == null)
|
||||
component = self.AddComponent<T>();
|
||||
return component;
|
||||
}
|
||||
|
||||
public static T GetOrAddComponent<T>(this MonoBehaviour self) where T : Component
|
||||
{
|
||||
return self.gameObject.GetOrAddComponent<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// true if the bit field or bit fields that are set in flags are also set in the current instance; otherwise, false.
|
||||
/// </summary>
|
||||
public static bool HasFlag(this Enum mask, Enum flags) // Same behavior than Enum.HasFlag is .NET 4
|
||||
{
|
||||
#if DEBUG
|
||||
if (mask.GetType() != flags.GetType())
|
||||
throw new System.ArgumentException(string.Format("The argument type, '{0}', is not the same as the enum type '{1}'.", flags.GetType(), mask.GetType()));
|
||||
#endif
|
||||
return ((int)(IConvertible)mask & (int)(IConvertible)flags) == (int)(IConvertible)flags;
|
||||
}
|
||||
|
||||
public static Vector2 xy(this Vector3 aVector) { return new Vector2(aVector.x, aVector.y); }
|
||||
public static Vector2 xz(this Vector3 aVector) { return new Vector2(aVector.x, aVector.z); }
|
||||
public static Vector2 yz(this Vector3 aVector) { return new Vector2(aVector.y, aVector.z); }
|
||||
public static Vector2 yx(this Vector3 aVector) { return new Vector2(aVector.y, aVector.x); }
|
||||
public static Vector2 zx(this Vector3 aVector) { return new Vector2(aVector.z, aVector.x); }
|
||||
public static Vector2 zy(this Vector3 aVector) { return new Vector2(aVector.z, aVector.y); }
|
||||
|
||||
const float kEpsilon = 0.00001f;
|
||||
public static bool Approximately(this float a, float b, float epsilon = kEpsilon) { return Mathf.Abs(a - b) < epsilon; }
|
||||
public static bool Approximately(this Vector2 a, Vector2 b, float epsilon = kEpsilon) { return Vector2.SqrMagnitude(a - b) < epsilon; }
|
||||
public static bool Approximately(this Vector3 a, Vector3 b, float epsilon = kEpsilon) { return Vector3.SqrMagnitude(a - b) < epsilon; }
|
||||
public static bool Approximately(this Vector4 a, Vector4 b, float epsilon = kEpsilon) { return Vector4.SqrMagnitude(a - b) < epsilon; }
|
||||
|
||||
public static Vector4 AsVector4(this Vector3 vec3, float w) { return new Vector4(vec3.x, vec3.y, vec3.z, w); }
|
||||
public static Vector4 PlaneEquation(Vector3 normalizedNormal, Vector3 pt) { return normalizedNormal.AsVector4(-Vector3.Dot(normalizedNormal, pt)); }
|
||||
|
||||
public static float GetVolumeCubic(this Bounds self) { return self.size.x * self.size.y * self.size.z; }
|
||||
public static float GetMaxArea2D(this Bounds self) { return Mathf.Max(Mathf.Max(self.size.x * self.size.y, self.size.y * self.size.z), self.size.x * self.size.z); }
|
||||
|
||||
public static Color Opaque(this Color self) { return new Color(self.r, self.g, self.b, 1f); }
|
||||
|
||||
public static Color ComputeComplementaryColor(this Color self, bool blackAndWhite)
|
||||
{
|
||||
if (blackAndWhite)
|
||||
{
|
||||
// http://stackoverflow.com/a/3943023/112731
|
||||
return (self.r * 0.299 + self.g * 0.587 + self.b * 0.114) > (186.0f / 255) ? Color.black : Color.white;
|
||||
}
|
||||
|
||||
return new Color(1.0f - self.r, 1.0f - self.g, 1.0f - self.b);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public static void GizmosDrawPlane(Vector3 normal, Vector3 position, Color color, Matrix4x4 mat, float size = 1f, float normalLength = 0.0f)
|
||||
{
|
||||
normal = normal.normalized;
|
||||
var prevMat = UnityEditor.Handles.matrix;
|
||||
var prevColor = UnityEditor.Handles.color;
|
||||
|
||||
UnityEditor.Handles.matrix = mat;
|
||||
UnityEditor.Handles.color = color;
|
||||
UnityEditor.Handles.RectangleHandleCap(0, position, Quaternion.LookRotation(normal), size, EventType.Repaint);
|
||||
|
||||
if (normalLength > 0.0f)
|
||||
{
|
||||
UnityEditor.Handles.DrawLine(position, position + normal * normalLength);
|
||||
UnityEditor.Handles.ConeHandleCap(0, position + normal * normalLength, Quaternion.LookRotation(normal), normalLength * 0.25f, EventType.Repaint);
|
||||
}
|
||||
|
||||
UnityEditor.Handles.matrix = prevMat;
|
||||
UnityEditor.Handles.color = prevColor;
|
||||
}
|
||||
#endif // UNITY_EDITOR
|
||||
|
||||
// Plane.Translate is not available in Unity 5
|
||||
public static Plane TranslateCustom(this Plane plane, Vector3 translation)
|
||||
{
|
||||
plane.distance += Vector3.Dot(translation.normalized, plane.normal) * translation.magnitude;
|
||||
return plane;
|
||||
}
|
||||
|
||||
// Plane.ClosestPointOnPlaneCustom is not available in Unity 5
|
||||
public static Vector3 ClosestPointOnPlaneCustom(this Plane plane, Vector3 point)
|
||||
{
|
||||
return point - plane.GetDistanceToPoint(point) * plane.normal;
|
||||
}
|
||||
|
||||
public static bool IsAlmostZero(float f) { return Mathf.Abs(f) < 0.001f; }
|
||||
|
||||
public static bool IsValid(this Plane plane)
|
||||
{
|
||||
return plane.normal.sqrMagnitude > 0.5f;
|
||||
}
|
||||
|
||||
public static void SetKeywordEnabled(this Material mat, string name, bool enabled)
|
||||
{
|
||||
if(enabled) mat.EnableKeyword(name);
|
||||
else mat.DisableKeyword(name);
|
||||
}
|
||||
|
||||
public static void SetShaderKeywordEnabled(string name, bool enabled)
|
||||
{
|
||||
if (enabled) Shader.EnableKeyword(name);
|
||||
else Shader.DisableKeyword(name);
|
||||
}
|
||||
|
||||
public static Matrix4x4 SampleInMatrix(this Gradient self, int floatPackingPrecision)
|
||||
{
|
||||
const int kSamplesCount = 16;
|
||||
var mat = new Matrix4x4();
|
||||
for (int i = 0; i < kSamplesCount; ++i)
|
||||
{
|
||||
var color = self.Evaluate(Mathf.Clamp01((float)(i) / (kSamplesCount - 1)));
|
||||
mat[i] = color.PackToFloat(floatPackingPrecision);
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
|
||||
public static Color[] SampleInArray(this Gradient self, int samplesCount)
|
||||
{
|
||||
var array = new Color[samplesCount];
|
||||
for (int i = 0; i < samplesCount; ++i)
|
||||
array[i] = self.Evaluate(Mathf.Clamp01((float)(i) / (samplesCount - 1)));
|
||||
return array;
|
||||
}
|
||||
|
||||
static Vector4 Vector4_Floor(Vector4 vec) { return new Vector4(Mathf.Floor(vec.x), Mathf.Floor(vec.y), Mathf.Floor(vec.z), Mathf.Floor(vec.w)); }
|
||||
|
||||
public static float PackToFloat(this Color color, int floatPackingPrecision)
|
||||
{
|
||||
Vector4 iVal = Vector4_Floor(color * (floatPackingPrecision - 1));
|
||||
|
||||
float output = 0;
|
||||
|
||||
output += iVal.x * floatPackingPrecision * floatPackingPrecision * floatPackingPrecision;
|
||||
output += iVal.y * floatPackingPrecision * floatPackingPrecision;
|
||||
output += iVal.z * floatPackingPrecision;
|
||||
output += iVal.w;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public enum FloatPackingPrecision { High = 64, Low = 8, Undef = 0 }
|
||||
static FloatPackingPrecision ms_FloatPackingPrecision = FloatPackingPrecision.Undef;
|
||||
|
||||
// OpenGL ES 2.0 GPU (graphicsShaderLevel = 30) usually have low float precision (16 bits on fragments)
|
||||
// So we lower the float packing precision on them (8 seems fine on Adreno (TM) 220, NVIDIA Tegra 3 and on Mali-450 MP)
|
||||
// https://docs.unity3d.com/Manual/SL-DataTypesAndPrecision.html
|
||||
const int kFloatPackingHighMinShaderLevel = 35;
|
||||
|
||||
public static FloatPackingPrecision GetFloatPackingPrecision()
|
||||
{
|
||||
if (ms_FloatPackingPrecision == FloatPackingPrecision.Undef)
|
||||
{
|
||||
ms_FloatPackingPrecision = SystemInfo.graphicsShaderLevel >= kFloatPackingHighMinShaderLevel ? FloatPackingPrecision.High : FloatPackingPrecision.Low;
|
||||
}
|
||||
return ms_FloatPackingPrecision;
|
||||
}
|
||||
|
||||
public static void MarkCurrentSceneDirty()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEngine.SceneManagement.SceneManager.GetActiveScene());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void MarkObjectDirty(UnityEngine.Object obj)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
UnityEditor.EditorUtility.SetDirty(obj);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public static bool IsEditorCamera(Camera cam)
|
||||
{
|
||||
var sceneView = UnityEditor.SceneView.currentDrawingSceneView;
|
||||
if (sceneView)
|
||||
{
|
||||
return cam == sceneView.camera;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void SetSameSceneVisibilityStatesThan(this GameObject self, GameObject model)
|
||||
{
|
||||
// SceneVisibilityManager is a feature available from 2019.2, but fixed for transient objects only from 2019.3.14f1
|
||||
// https://issuetracker.unity3d.com/issues/toggling-of-picking-and-visibility-flags-of-a-gameobject-is-ignored-when-gameobject-dot-hideflags-is-set-to-hideflags-dot-dontsave
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
bool pickingDisabled = UnityEditor.SceneVisibilityManager.instance.IsPickingDisabled(model);
|
||||
if (pickingDisabled) UnityEditor.SceneVisibilityManager.instance.DisablePicking(self, true);
|
||||
else UnityEditor.SceneVisibilityManager.instance.EnablePicking(self, true);
|
||||
|
||||
bool hidden = UnityEditor.SceneVisibilityManager.instance.IsHidden(model);
|
||||
if (hidden) UnityEditor.SceneVisibilityManager.instance.Hide(self, true);
|
||||
else UnityEditor.SceneVisibilityManager.instance.Show(self, true);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8a3c52d4dd2a02a4499d1c94fcc9c542
|
||||
timeCreated: 1504688222
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace VLB
|
||||
{
|
||||
public static class Version
|
||||
{
|
||||
public const int Current = 1960;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c62ea6a423ab5824c9d344b83ee9706e
|
||||
timeCreated: 1507996747
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,404 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
[DisallowMultipleComponent]
|
||||
[RequireComponent(typeof(VolumetricLightBeam))]
|
||||
[HelpURL(Consts.Help.UrlDustParticles)]
|
||||
public class VolumetricDustParticles : MonoBehaviour
|
||||
{
|
||||
public const string ClassName = "VolumetricDustParticles";
|
||||
|
||||
/// <summary>
|
||||
/// Max alpha of the particles
|
||||
/// </summary>
|
||||
[Range(0f, 1f)]
|
||||
public float alpha = Consts.DustParticles.AlphaDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Max size of the particles
|
||||
/// </summary>
|
||||
[Range(0.0001f, 0.1f)]
|
||||
public float size = Consts.DustParticles.SizeDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Direction of the particles.
|
||||
/// </summary>
|
||||
public ParticlesDirection direction = Consts.DustParticles.DirectionDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Movement speed of the particles.
|
||||
/// </summary>
|
||||
public Vector3 velocity = Consts.DustParticles.VelocityDefault;
|
||||
|
||||
[System.Obsolete("Use 'velocity' instead")]
|
||||
public float speed = 0.03f;
|
||||
|
||||
/// <summary>
|
||||
/// Control how many particles are spawned. The higher the density, the more particles are spawned, the higher the performance cost is.
|
||||
/// </summary>
|
||||
public float density = Consts.DustParticles.DensityDefault;
|
||||
|
||||
/// <summary>
|
||||
/// The distance range (from the light source) where the particles are spawned.
|
||||
/// - Min bound: the higher it is, the more the particles are spawned away from the light source.
|
||||
/// - Max bound: the lower it is, the more the particles are gathered near the light source.
|
||||
/// </summary>
|
||||
[MinMaxRange(0.0f, 1.0f)]
|
||||
public MinMaxRangeFloat spawnDistanceRange = Consts.DustParticles.SpawnDistanceRangeDefault;
|
||||
|
||||
[System.Obsolete("Use 'spawnDistanceRange' instead")]
|
||||
public float spawnMinDistance = 0f;
|
||||
|
||||
[System.Obsolete("Use 'spawnDistanceRange' instead")]
|
||||
public float spawnMaxDistance = 0.7f;
|
||||
|
||||
/// <summary>
|
||||
/// Enable particles culling based on the distance to the Main Camera.
|
||||
/// We highly recommend to enable this feature to keep good runtime performances.
|
||||
/// </summary>
|
||||
public bool cullingEnabled = Consts.DustParticles.CullingEnabledDefault;
|
||||
|
||||
/// <summary>
|
||||
/// If culling is enabled, the particles will not be rendered if they are further than cullingMaxDistance to the Main Camera.
|
||||
/// </summary>
|
||||
public float cullingMaxDistance = Consts.DustParticles.CullingMaxDistanceDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Is the particle system currently culled (no visible) because too far from the main camera?
|
||||
/// </summary>
|
||||
public bool isCulled { get; private set; }
|
||||
|
||||
|
||||
[SerializeField] float m_AlphaAdditionalRuntime = 1.0f;
|
||||
public float alphaAdditionalRuntime
|
||||
{
|
||||
get { return m_AlphaAdditionalRuntime; }
|
||||
set { if (m_AlphaAdditionalRuntime != value) { m_AlphaAdditionalRuntime = value; m_RuntimePropertiesDirty = true; } }
|
||||
}
|
||||
|
||||
public bool particlesAreInstantiated { get { return m_Particles; } }
|
||||
public int particlesCurrentCount { get { return m_Particles ? m_Particles.particleCount : 0; } }
|
||||
public int particlesMaxCount { get { return m_Particles ? m_Particles.main.maxParticles : 0; } }
|
||||
ParticleSystem m_Particles = null;
|
||||
ParticleSystemRenderer m_Renderer = null;
|
||||
Material m_Material = null;
|
||||
Gradient m_GradientCached = new Gradient();
|
||||
bool m_RuntimePropertiesDirty = true;
|
||||
|
||||
|
||||
/// <summary>Cached version of Camera.main, for performance reasons</summary>
|
||||
public Camera mainCamera
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!ms_MainCamera)
|
||||
{
|
||||
ms_MainCamera = Camera.main;
|
||||
if (!ms_MainCamera && !ms_NoMainCameraLogged)
|
||||
{
|
||||
Debug.LogErrorFormat(gameObject, "In order to use '" + VolumetricDustParticles.ClassName + "' culling, you must have a MainCamera defined in your scene.");
|
||||
ms_NoMainCameraLogged = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ms_MainCamera;
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the main camera, because accessing Camera.main at each frame is very bad performance-wise
|
||||
static bool ms_NoMainCameraLogged = false;
|
||||
static Camera ms_MainCamera = null;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void OnValidate()
|
||||
{
|
||||
density = Mathf.Clamp(density, Consts.DustParticles.DensityMin, Consts.DustParticles.DensityMax);
|
||||
cullingMaxDistance = Mathf.Max(cullingMaxDistance, Consts.DustParticles.CullingMaxDistanceMin);
|
||||
Play(); // support instant refresh when modifying properties from inspector
|
||||
}
|
||||
#endif // UNITY_EDITOR
|
||||
|
||||
VolumetricLightBeam m_Master = null;
|
||||
|
||||
void Start()
|
||||
{
|
||||
isCulled = false;
|
||||
|
||||
m_Master = GetComponent<VolumetricLightBeam>();
|
||||
Debug.Assert(m_Master);
|
||||
HandleBackwardCompatibility(m_Master._INTERNAL_pluginVersion, Version.Current);
|
||||
|
||||
InstantiateParticleSystem();
|
||||
|
||||
SetActiveAndPlay();
|
||||
}
|
||||
|
||||
void InstantiateParticleSystem()
|
||||
{
|
||||
// If we duplicate (from Editor and Playmode) the VLB, the children are also duplicated (like the dust particles)
|
||||
// we have to make sure to properly destroy them before creating our proper procedural particle instance.
|
||||
var slaves = GetComponentsInChildren<ParticleSystem>(true);
|
||||
for (int i = slaves.Length - 1; i >= 0; --i)
|
||||
DestroyImmediate(slaves[i].gameObject);
|
||||
|
||||
m_Particles = Config.Instance.NewVolumetricDustParticles();
|
||||
|
||||
if (m_Particles)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (m_Master)
|
||||
{
|
||||
UnityEditor.GameObjectUtility.SetStaticEditorFlags(m_Particles.gameObject, m_Master.GetStaticEditorFlagsForSubObjects());
|
||||
m_Particles.gameObject.SetSameSceneVisibilityStatesThan(m_Master.gameObject);
|
||||
}
|
||||
#endif // UNITY_EDITOR
|
||||
m_Particles.transform.SetParent(transform, false);
|
||||
m_Particles.transform.localRotation = m_Master.beamInternalLocalRotation;
|
||||
|
||||
m_Renderer = m_Particles.GetComponent<ParticleSystemRenderer>();
|
||||
Debug.Assert(m_Renderer);
|
||||
|
||||
m_Material = new Material(m_Renderer.sharedMaterial);
|
||||
Debug.Assert(m_Material);
|
||||
m_Renderer.material = m_Material;
|
||||
}
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
SetActiveAndPlay();
|
||||
}
|
||||
|
||||
void SetActive(bool active)
|
||||
{
|
||||
if (m_Particles) m_Particles.gameObject.SetActive(active);
|
||||
}
|
||||
|
||||
void SetActiveAndPlay()
|
||||
{
|
||||
SetActive(true);
|
||||
Play();
|
||||
}
|
||||
|
||||
void Play()
|
||||
{
|
||||
if (m_Particles)
|
||||
{
|
||||
SetParticleProperties();
|
||||
m_Particles.Simulate(0f);
|
||||
m_Particles.Play(true);
|
||||
}
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
SetActive(false);
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (m_Particles)
|
||||
{
|
||||
DestroyImmediate(m_Particles.gameObject); // Make sure to delete the GAO
|
||||
m_Particles = null;
|
||||
}
|
||||
|
||||
if (m_Material)
|
||||
{
|
||||
DestroyImmediate(m_Material);
|
||||
m_Material = null;
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if(!Application.isPlaying)
|
||||
{
|
||||
if (m_Particles == null)
|
||||
InstantiateParticleSystem();
|
||||
|
||||
Play();
|
||||
}
|
||||
else
|
||||
#endif // UNITY_EDITOR
|
||||
{
|
||||
UpdateCulling();
|
||||
|
||||
Debug.Assert(m_Master);
|
||||
if (m_Master.trackChangesDuringPlaytime)
|
||||
SetParticleProperties();
|
||||
}
|
||||
|
||||
if (m_RuntimePropertiesDirty && m_Material != null)
|
||||
{
|
||||
m_Material.SetColor(ShaderProperties.ParticlesTintColor, new Color(1.0f, 1.0f, 1.0f, alphaAdditionalRuntime));
|
||||
m_RuntimePropertiesDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SetParticleProperties()
|
||||
{
|
||||
if (m_Particles && m_Particles.gameObject.activeSelf)
|
||||
{
|
||||
var thickness = Mathf.Clamp01(1 - (m_Master.fresnelPow / Consts.Beam.FresnelPowMaxValue));
|
||||
|
||||
var coneLength = m_Master.fallOffEnd * (spawnDistanceRange.maxValue - spawnDistanceRange.minValue);
|
||||
var ratePerSec = coneLength * density;
|
||||
int maxParticles = (int)(ratePerSec * 4);
|
||||
|
||||
var main = m_Particles.main;
|
||||
|
||||
var startLifetime = main.startLifetime;
|
||||
startLifetime.mode = ParticleSystemCurveMode.TwoConstants;
|
||||
startLifetime.constantMin = 4f;
|
||||
startLifetime.constantMax = 6f;
|
||||
main.startLifetime = startLifetime;
|
||||
|
||||
var startSize = main.startSize;
|
||||
startSize.mode = ParticleSystemCurveMode.TwoConstants;
|
||||
startSize.constantMin = size * 0.9f;
|
||||
startSize.constantMax = size * 1.1f;
|
||||
main.startSize = startSize;
|
||||
|
||||
var startColor = main.startColor;
|
||||
|
||||
if (m_Master.usedColorMode == ColorMode.Flat)
|
||||
{
|
||||
startColor.mode = ParticleSystemGradientMode.Color;
|
||||
var colorMax = m_Master.color;
|
||||
colorMax.a *= alpha;
|
||||
startColor.color = colorMax;
|
||||
}
|
||||
else
|
||||
{
|
||||
startColor.mode = ParticleSystemGradientMode.Gradient;
|
||||
|
||||
// Duplicate gradient and apply alpha
|
||||
var gradientRef = m_Master.colorGradient;
|
||||
var colorKeys = gradientRef.colorKeys;
|
||||
var alphaKeys = gradientRef.alphaKeys;
|
||||
|
||||
for(int i=0; i< alphaKeys.Length; ++i)
|
||||
alphaKeys[i].alpha *= alpha;
|
||||
|
||||
Debug.Assert(m_GradientCached != null);
|
||||
m_GradientCached.SetKeys(colorKeys, alphaKeys);
|
||||
startColor.gradient = m_GradientCached;
|
||||
}
|
||||
main.startColor = startColor;
|
||||
|
||||
{
|
||||
var startSpeed = main.startSpeed;
|
||||
startSpeed.constant = (direction == ParticlesDirection.Random) ? Mathf.Abs(velocity.z) : 0.0f;
|
||||
main.startSpeed = startSpeed;
|
||||
}
|
||||
|
||||
{
|
||||
var velocityOverLifetime = m_Particles.velocityOverLifetime;
|
||||
velocityOverLifetime.enabled = (direction != ParticlesDirection.Random);
|
||||
velocityOverLifetime.space = (direction == ParticlesDirection.LocalSpace) ? ParticleSystemSimulationSpace.Local : ParticleSystemSimulationSpace.World;
|
||||
velocityOverLifetime.xMultiplier = velocity.x;
|
||||
velocityOverLifetime.yMultiplier = velocity.y;
|
||||
velocityOverLifetime.zMultiplier = velocity.z;
|
||||
}
|
||||
|
||||
main.maxParticles = maxParticles;
|
||||
|
||||
{
|
||||
var shape = m_Particles.shape;
|
||||
shape.shapeType = ParticleSystemShapeType.ConeVolume;
|
||||
|
||||
float coneAngle = m_Master.coneAngle * Mathf.Lerp(0.7f, 1f, thickness);
|
||||
shape.angle = coneAngle * 0.5f;
|
||||
|
||||
float radiusStart = m_Master.coneRadiusStart * Mathf.Lerp(0.3f, 1.0f, thickness);
|
||||
float radiusEnd = Utils.ComputeConeRadiusEnd(m_Master.fallOffEnd, coneAngle);
|
||||
shape.radius = Mathf.Lerp(radiusStart, radiusEnd, spawnDistanceRange.minValue);
|
||||
|
||||
shape.length = coneLength;
|
||||
|
||||
var localOffset = m_Master.fallOffEnd * spawnDistanceRange.minValue;
|
||||
#if UNITY_2017_1_OR_NEWER
|
||||
shape.position = new Vector3(0f, 0f, localOffset);
|
||||
#else
|
||||
m_Particles.transform.localPosition = m_Master.beamLocalForward * localOffset;
|
||||
#endif
|
||||
shape.arc = 360f;
|
||||
shape.randomDirectionAmount = (direction == ParticlesDirection.Random) ? 1f : 0f;
|
||||
}
|
||||
|
||||
var emission = m_Particles.emission;
|
||||
var rate = emission.rateOverTime;
|
||||
rate.constant = ratePerSec;
|
||||
emission.rateOverTime = rate;
|
||||
|
||||
if(m_Renderer)
|
||||
{
|
||||
m_Renderer.sortingLayerID = m_Master.sortingLayerID;
|
||||
m_Renderer.sortingOrder = m_Master.sortingOrder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandleBackwardCompatibility(int serializedVersion, int newVersion)
|
||||
{
|
||||
if (serializedVersion == -1) return; // freshly new spawned entity: nothing to do
|
||||
if (serializedVersion == newVersion) return; // same version: nothing to do
|
||||
|
||||
#pragma warning disable 0618
|
||||
if (serializedVersion < 1880)
|
||||
{
|
||||
// Version 1880 changed the order of ParticlesDirection enum and add WorldSpace option
|
||||
if ((int)direction == 0) direction = (ParticlesDirection)1;
|
||||
else direction = (ParticlesDirection)0;
|
||||
|
||||
// Version 1880 changed from single float speed to 3D velocity vector
|
||||
velocity = new Vector3(0.0f, 0.0f, speed);
|
||||
}
|
||||
|
||||
if (serializedVersion < 1940)
|
||||
{
|
||||
spawnDistanceRange = new MinMaxRangeFloat(spawnMinDistance, spawnMaxDistance);
|
||||
}
|
||||
#pragma warning restore 0618
|
||||
|
||||
Utils.MarkCurrentSceneDirty();
|
||||
}
|
||||
|
||||
#region Culling
|
||||
void UpdateCulling()
|
||||
{
|
||||
if (m_Particles)
|
||||
{
|
||||
bool visible = true;
|
||||
if ((cullingEnabled || m_Master.isFadeOutEnabled) && m_Master.hasGeometry)
|
||||
{
|
||||
if (mainCamera)
|
||||
{
|
||||
var maxDist = cullingMaxDistance;
|
||||
if (m_Master.isFadeOutEnabled) maxDist = Mathf.Min(maxDist, m_Master.fadeOutEnd);
|
||||
var maxDistSqr = maxDist * maxDist;
|
||||
var distSqr = m_Master.bounds.SqrDistance(mainCamera.transform.position);
|
||||
visible = distSqr <= maxDistSqr;
|
||||
}
|
||||
else
|
||||
cullingEnabled = false;
|
||||
}
|
||||
|
||||
if (m_Particles.gameObject.activeSelf != visible)
|
||||
{
|
||||
SetActive(visible);
|
||||
isCulled = !visible;
|
||||
}
|
||||
|
||||
if (visible && !m_Particles.isPlaying)
|
||||
m_Particles.Play();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e39f0f7e17f11f2448932da58e9b63d4
|
||||
labels:
|
||||
- volumetric
|
||||
- light
|
||||
- lighting
|
||||
- ray
|
||||
- shaft
|
||||
- beam
|
||||
- density
|
||||
- vr
|
||||
- dynamic
|
||||
- spot
|
||||
- spotlight
|
||||
- fog
|
||||
- noise
|
||||
- occlusion
|
||||
- procedural
|
||||
timeCreated: 1511563236
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 54d50eb83191723439a591eeeb0eab84, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
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: Store
|
||||
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