868 lines
34 KiB
C#
868 lines
34 KiB
C#
#if UNITY_EDITOR
|
|
//#define PROFILE_INSTANCE_LOADING
|
|
using UnityEditor;
|
|
#endif
|
|
using UnityEngine;
|
|
using UnityEngine.Serialization;
|
|
|
|
#if VLB_URP
|
|
using UnityEngine.Rendering.Universal;
|
|
#endif
|
|
|
|
namespace VLB
|
|
{
|
|
[HelpURL(Consts.Help.UrlConfig)]
|
|
public class Config : ScriptableObject
|
|
{
|
|
public const string ClassName = "Config";
|
|
|
|
public const string kAssetName = "VLBConfigOverride";
|
|
public const string kAssetNameExt = ".asset";
|
|
|
|
/// <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;
|
|
public int geometryRenderQueueHD = (int)Consts.Config.HD.GeometryRenderQueueDefault;
|
|
|
|
/// <summary>
|
|
/// Select the Render Pipeline (Built-In or SRP) in use.
|
|
/// </summary>
|
|
public RenderPipeline renderPipeline
|
|
{
|
|
get { return m_RenderPipeline; }
|
|
set
|
|
{
|
|
#if UNITY_EDITOR
|
|
m_RenderPipeline = value;
|
|
#else
|
|
Debug.LogError("Modifying the RenderPipeline in standalone builds is not permitted");
|
|
#endif
|
|
}
|
|
}
|
|
[FormerlySerializedAs("renderPipeline"), FormerlySerializedAs("_RenderPipeline")]
|
|
[SerializeField] RenderPipeline m_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 m_RenderingMode; }
|
|
set
|
|
{
|
|
#if UNITY_EDITOR
|
|
m_RenderingMode = value;
|
|
#else
|
|
Debug.LogError("Modifying the RenderingMode in standalone builds is not permitted");
|
|
#endif
|
|
}
|
|
}
|
|
[FormerlySerializedAs("renderingMode"), FormerlySerializedAs("_RenderingMode")]
|
|
[SerializeField] RenderingMode m_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.projectRenderPipeline;
|
|
return rp == RenderPipeline.URP || rp == RenderPipeline.HDRP;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Actual Rendering Mode used on the current platform
|
|
/// </summary>
|
|
public RenderingMode GetActualRenderingMode(ShaderMode shaderMode)
|
|
{
|
|
if (renderingMode == RenderingMode.SRPBatcher && !IsSRPBatcherSupported()) return RenderingMode.Default;
|
|
|
|
// Using a Scriptable Render Pipeline with 'Multi-Pass' Rendering Mode is not supported
|
|
if (renderPipeline != RenderPipeline.BuiltIn && renderingMode == RenderingMode.MultiPass) return RenderingMode.Default;
|
|
|
|
// HD beams require single pass shaders
|
|
if (shaderMode == ShaderMode.HD && renderingMode == RenderingMode.MultiPass) return RenderingMode.Default;
|
|
|
|
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 SD_useSinglePassShader { get { return GetActualRenderingMode(ShaderMode.SD) != RenderingMode.MultiPass; } }
|
|
|
|
public bool SD_requiresDoubleSidedMesh { get { return SD_useSinglePassShader; } }
|
|
|
|
/// <summary>
|
|
/// Main shader applied to the cone beam geometry
|
|
/// </summary>
|
|
public Shader GetBeamShader(ShaderMode mode)
|
|
{
|
|
#if UNITY_EDITOR
|
|
var shader = GetBeamShaderInternal(mode);
|
|
if (shader == null)
|
|
RefreshShader(mode, RefreshShaderFlags.All);
|
|
return shader;
|
|
#else
|
|
return GetBeamShaderInternal(mode);
|
|
#endif
|
|
}
|
|
|
|
ref Shader GetBeamShaderInternal(ShaderMode mode)
|
|
{
|
|
if(mode == ShaderMode.SD) return ref _BeamShader;
|
|
else return ref _BeamShaderHD;
|
|
}
|
|
|
|
int GetRenderQueueInternal(ShaderMode mode)
|
|
{
|
|
if (mode == ShaderMode.SD) return geometryRenderQueue;
|
|
else return geometryRenderQueueHD;
|
|
}
|
|
|
|
public Material NewMaterialTransient(ShaderMode mode, bool gpuInstanced)
|
|
{
|
|
var material = MaterialManager.NewMaterialPersistent(GetBeamShader(mode), gpuInstanced);
|
|
if (material)
|
|
{
|
|
material.hideFlags = Consts.Internal.ProceduralObjectsHideFlags;
|
|
material.renderQueue = GetRenderQueueInternal(mode);
|
|
}
|
|
return material;
|
|
}
|
|
|
|
/// <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>
|
|
/// Contribution of the attached spotlight temperature to the final beam color.
|
|
/// Only useful when:
|
|
/// - The beams is attached to a Unity spotlight.
|
|
/// - The beams color is linked to the Unity Light color.
|
|
/// - The Unity light uses 'color temperature mode' and is specified with 'Filter' and 'Temperature' properties.
|
|
/// </summary>
|
|
public bool useLightColorTemperature = Consts.Config.UseLightColorTemperatureDefault;
|
|
|
|
/// <summary>
|
|
/// Number of Sides of the shared cone mesh
|
|
/// </summary>
|
|
public int sharedMeshSides = Consts.Config.SharedMeshSidesDefault;
|
|
|
|
/// <summary>
|
|
/// Number of Segments of the shared cone mesh
|
|
/// </summary>
|
|
public int sharedMeshSegments = Consts.Config.SharedMeshSegmentsDefault;
|
|
|
|
/// <summary>
|
|
/// Distance from the camera the beam will fade (for HD beams only, for SD beams, this option can be configured per beam)
|
|
/// 0 = hard intersection
|
|
/// Higher values produce soft intersection when the camera is near the cone triangles.
|
|
/// </summary>
|
|
public float hdBeamsCameraBlendingDistance = Consts.Config.HD.CameraBlendingDistance;
|
|
|
|
/// <summary>
|
|
/// When using URP, specify a custom Renderer index used by the depth cameras for the 'Dynamic Occlusion (Depth Buffer)' with SD beams and 'Volumetric Shadow' for HD Beams features.
|
|
/// The 'Renderer list' is editable in the URP asset.
|
|
/// We recommend to specify a custom index referencing the URP default 'ForwardRenderer' when you are using a custom renderer that doesn't support writing to depth render texture.
|
|
/// This is the case if you encounter errors like: 'RenderTexture.Create failed: colorFormat & depthStencilFormat cannot both be none.'
|
|
/// Set -1 to disable this feature.
|
|
/// </summary>
|
|
public int urpDepthCameraScriptableRendererIndex = -1;
|
|
|
|
public void SetURPScriptableRendererIndexToDepthCamera(Camera camera)
|
|
{
|
|
#if VLB_URP
|
|
if (urpDepthCameraScriptableRendererIndex < 0)
|
|
return;
|
|
|
|
Debug.Assert(camera);
|
|
var cameraData = camera.GetUniversalAdditionalCameraData();
|
|
if (cameraData)
|
|
{
|
|
cameraData.SetRenderer(urpDepthCameraScriptableRendererIndex);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/// <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>
|
|
[HighlightNull]
|
|
public Texture2D ditheringNoiseTexture = null;
|
|
|
|
[HighlightNull]
|
|
public Texture2D jitteringNoiseTexture = 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;
|
|
|
|
/// <summary>
|
|
/// Support 'Shadow' features or not.
|
|
/// </summary>
|
|
public bool featureEnabledShadow = true;
|
|
|
|
/// <summary>
|
|
/// Support 'Cookie' feature or not.
|
|
/// </summary>
|
|
public bool featureEnabledCookie = true;
|
|
|
|
|
|
|
|
/// RAYMARCHING BEGIN
|
|
[SerializeField] RaymarchingQuality[] m_RaymarchingQualities = null;
|
|
|
|
[SerializeField] int m_DefaultRaymarchingQualityUniqueID = 0;
|
|
|
|
public int defaultRaymarchingQualityUniqueID => m_DefaultRaymarchingQualityUniqueID;
|
|
|
|
public RaymarchingQuality GetRaymarchingQualityForIndex(int index)
|
|
{
|
|
Debug.Assert(index >= 0);
|
|
Debug.Assert(m_RaymarchingQualities != null);
|
|
Debug.Assert(index < m_RaymarchingQualities.Length);
|
|
return m_RaymarchingQualities[index];
|
|
}
|
|
|
|
public RaymarchingQuality GetRaymarchingQualityForUniqueID(int id)
|
|
{
|
|
int index = GetRaymarchingQualityIndexForUniqueID(id);
|
|
if (index >= 0)
|
|
return GetRaymarchingQualityForIndex(index);
|
|
return null;
|
|
}
|
|
|
|
public int GetRaymarchingQualityIndexForUniqueID(int id)
|
|
{
|
|
for (int i = 0; i < m_RaymarchingQualities.Length; ++i)
|
|
{
|
|
var qual = m_RaymarchingQualities[i];
|
|
if (qual != null && qual.uniqueID == id)
|
|
return i;
|
|
}
|
|
|
|
Debug.LogErrorFormat("Failed to find RaymarchingQualityIndex for Unique ID {0}", id);
|
|
return -1;
|
|
}
|
|
|
|
public bool IsRaymarchingQualityUniqueIDValid(int id) { return GetRaymarchingQualityIndexForUniqueID(id) >= 0; }
|
|
|
|
#if UNITY_EDITOR
|
|
public void AddRaymarchingQuality(RaymarchingQuality qual)
|
|
{
|
|
ArrayUtility.Add(ref m_RaymarchingQualities, qual);
|
|
}
|
|
|
|
public void RemoveRaymarchingQualityAtIndex(int index)
|
|
{
|
|
Debug.Assert(index >= 0);
|
|
Debug.Assert(index < m_RaymarchingQualities.Length);
|
|
ArrayUtility.RemoveAt(ref m_RaymarchingQualities, index);
|
|
}
|
|
#endif
|
|
|
|
public int raymarchingQualitiesCount { get { return Mathf.Max(1, m_RaymarchingQualities != null ? m_RaymarchingQualities.Length : 1); } }
|
|
|
|
void CreateDefaultRaymarchingQualityPreset(bool onlyIfNeeded)
|
|
{
|
|
if (m_RaymarchingQualities == null || m_RaymarchingQualities.Length == 0 || !onlyIfNeeded)
|
|
{
|
|
m_RaymarchingQualities = new RaymarchingQuality[3];
|
|
// set forced unique ID for default qualities to keep finding them even when deleting Config instance
|
|
m_RaymarchingQualities[0] = RaymarchingQuality.New("Fast", 1, 5);
|
|
m_RaymarchingQualities[1] = RaymarchingQuality.New("Balanced", 2, 10);
|
|
m_RaymarchingQualities[2] = RaymarchingQuality.New("High", 3, 20);
|
|
m_DefaultRaymarchingQualityUniqueID = m_RaymarchingQualities[1].uniqueID;
|
|
}
|
|
}
|
|
/// RAYMARCHING END
|
|
|
|
|
|
public bool isHDRPExposureWeightSupported
|
|
{
|
|
get
|
|
{
|
|
#if UNITY_2021_1_OR_NEWER
|
|
return renderPipeline == RenderPipeline.HDRP;
|
|
#else
|
|
return false; // GetCurrentExposureMultiplier is accessible but doesn't return a proper value in Unity 2020 for some reasons
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// INTERNAL
|
|
#pragma warning disable 0414
|
|
[SerializeField] int pluginVersion = -1;
|
|
[SerializeField] Material _DummyMaterial = null;
|
|
[SerializeField] Material _DummyMaterialHD = null;
|
|
[SerializeField] Shader _BeamShader = null;
|
|
[SerializeField] Shader _BeamShaderHD = null;
|
|
#pragma warning restore 0414
|
|
|
|
Transform m_CachedFadeOutCamera = null;
|
|
|
|
public bool hasRenderPipelineMismatch { get { return (SRPHelper.projectRenderPipeline == RenderPipeline.BuiltIn) != (m_RenderPipeline == RenderPipeline.BuiltIn); } }
|
|
|
|
[RuntimeInitializeOnLoadMethod]
|
|
static void OnStartup()
|
|
{
|
|
Instance.m_CachedFadeOutCamera = null;
|
|
Instance.RefreshGlobalShaderProperties();
|
|
|
|
#if UNITY_EDITOR
|
|
Instance.RefreshShaders(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);
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
[InitializeOnLoadMethod]
|
|
static void OnProjectLoadedInEditor()
|
|
{
|
|
// Code executed on Unity Editor startup
|
|
// use the static variable and NOT the Instance property to prevent from creating a Config instance right away when you unpack the plugin,
|
|
// otherwise other assets (noise texture...) might not be loaded and references can be broken
|
|
if (ms_Instance)
|
|
ms_Instance.SetScriptingDefineSymbolsForCurrentRenderPipeline();
|
|
}
|
|
|
|
public void SetScriptingDefineSymbolsForCurrentRenderPipeline()
|
|
{
|
|
SRPHelper.SetScriptingDefineSymbolsForRenderPipeline(renderPipeline);
|
|
}
|
|
#endif
|
|
|
|
public void Reset()
|
|
{
|
|
geometryOverrideLayer = Consts.Config.GeometryOverrideLayerDefault;
|
|
geometryLayerID = Consts.Config.GeometryLayerIDDefault;
|
|
geometryTag = Consts.Config.GeometryTagDefault;
|
|
geometryRenderQueue = (int)Consts.Config.GeometryRenderQueueDefault;
|
|
geometryRenderQueueHD = (int)Consts.Config.HD.GeometryRenderQueueDefault;
|
|
|
|
sharedMeshSides = Consts.Config.SharedMeshSidesDefault;
|
|
sharedMeshSegments = Consts.Config.SharedMeshSegmentsDefault;
|
|
|
|
globalNoiseScale = Consts.Beam.NoiseScaleDefault;
|
|
globalNoiseVelocity = Consts.Beam.NoiseVelocityDefault;
|
|
|
|
renderPipeline = Consts.Config.GeometryRenderPipelineDefault;
|
|
renderingMode = Consts.Config.GeometryRenderingModeDefault;
|
|
ditheringFactor = Consts.Config.DitheringFactor;
|
|
useLightColorTemperature = Consts.Config.UseLightColorTemperatureDefault;
|
|
|
|
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;
|
|
|
|
hdBeamsCameraBlendingDistance = Consts.Config.HD.CameraBlendingDistance;
|
|
urpDepthCameraScriptableRendererIndex = -1;
|
|
|
|
CreateDefaultRaymarchingQualityPreset(onlyIfNeeded: false);
|
|
|
|
ResetInternalData();
|
|
|
|
#if UNITY_EDITOR
|
|
GlobalMeshSD.Destroy();
|
|
Utils._EditorSetAllMeshesDirty();
|
|
#endif
|
|
}
|
|
|
|
void RefreshGlobalShaderProperties()
|
|
{
|
|
Shader.SetGlobalFloat(ShaderProperties.GlobalUsesReversedZBuffer, SystemInfo.usesReversedZBuffer ? 1.0f : 0.0f);
|
|
Shader.SetGlobalFloat(ShaderProperties.GlobalDitheringFactor, ditheringFactor);
|
|
Shader.SetGlobalTexture(ShaderProperties.GlobalDitheringNoiseTex, ditheringNoiseTexture);
|
|
|
|
Shader.SetGlobalFloat(ShaderProperties.HD.GlobalCameraBlendingDistance, hdBeamsCameraBlendingDistance);
|
|
Shader.SetGlobalTexture(ShaderProperties.HD.GlobalJitteringNoiseTex, jitteringNoiseTexture);
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
public void _EditorSetRenderingModeAndRefreshShader(RenderingMode mode)
|
|
{
|
|
renderingMode = mode;
|
|
RefreshShaders(RefreshShaderFlags.All);
|
|
}
|
|
|
|
void OnValidate()
|
|
{
|
|
sharedMeshSides = Mathf.Clamp(sharedMeshSides, Consts.Config.SharedMeshSidesMin, Consts.Config.SharedMeshSidesMax);
|
|
sharedMeshSegments = Mathf.Clamp(sharedMeshSegments, Consts.Config.SharedMeshSegmentsMin, Consts.Config.SharedMeshSegmentsMax);
|
|
|
|
ditheringFactor = Mathf.Clamp01(ditheringFactor);
|
|
|
|
hdBeamsCameraBlendingDistance = Mathf.Max(hdBeamsCameraBlendingDistance, 0f);
|
|
}
|
|
|
|
void AutoSelectRenderPipeline()
|
|
{
|
|
var newPipeline = SRPHelper.projectRenderPipeline;
|
|
if (newPipeline != renderPipeline)
|
|
{
|
|
renderPipeline = newPipeline;
|
|
EditorUtility.SetDirty(this); // make sure to save this property change
|
|
RefreshShaders(RefreshShaderFlags.All);
|
|
SetScriptingDefineSymbolsForCurrentRenderPipeline();
|
|
}
|
|
}
|
|
|
|
public static void EditorSelectInstance()
|
|
{
|
|
Selection.activeObject = Instance; // this will create the instance if it doesn't exist
|
|
if (Selection.activeObject == null)
|
|
Debug.LogError("Cannot find any Config resource");
|
|
}
|
|
|
|
ref Material GetDummyMaterial(ShaderMode shaderMode)
|
|
{
|
|
if (shaderMode == ShaderMode.SD) return ref _DummyMaterial;
|
|
else return ref _DummyMaterialHD;
|
|
}
|
|
|
|
[System.Flags]
|
|
public enum RefreshShaderFlags
|
|
{
|
|
Reference = 1 << 1,
|
|
Dummy = 1 << 2,
|
|
All = Reference | Dummy,
|
|
}
|
|
|
|
public void RefreshShaders(RefreshShaderFlags flags)
|
|
{
|
|
foreach (ShaderMode shaderMode in System.Enum.GetValues(typeof(ShaderMode)))
|
|
RefreshShader(shaderMode, flags);
|
|
}
|
|
|
|
public void RefreshShader(ShaderMode shaderMode, RefreshShaderFlags flags)
|
|
{
|
|
ref Shader shader = ref GetBeamShaderInternal(shaderMode);
|
|
|
|
if (flags.HasFlag(RefreshShaderFlags.Reference))
|
|
{
|
|
var prevShader = shader;
|
|
|
|
var configProps = new ShaderGenerator.ConfigProps
|
|
{
|
|
renderPipeline = m_RenderPipeline,
|
|
renderingMode = GetActualRenderingMode(shaderMode),
|
|
dithering = ditheringFactor > 0.0f,
|
|
noise3D = featureEnabledNoise3D,
|
|
colorGradient = featureEnabledColorGradient,
|
|
depthBlend = featureEnabledDepthBlend,
|
|
dynamicOcclusion = featureEnabledDynamicOcclusion,
|
|
meshSkewing = featureEnabledMeshSkewing,
|
|
shaderAccuracyHigh = featureEnabledShaderAccuracyHigh,
|
|
cookie = featureEnabledCookie,
|
|
shadow = featureEnabledShadow,
|
|
raymarchingQualities = m_RaymarchingQualities
|
|
};
|
|
|
|
shader = ShaderGenerator.Generate(shaderMode, configProps);
|
|
|
|
if (shader != prevShader)
|
|
{
|
|
EditorUtility.SetDirty(this);
|
|
}
|
|
}
|
|
|
|
if (flags.HasFlag(RefreshShaderFlags.Dummy) && shader != null)
|
|
{
|
|
bool gpuInstanced = GetActualRenderingMode(shaderMode) == RenderingMode.GPUInstancing;
|
|
ref var dummyMat = ref GetDummyMaterial(shaderMode);
|
|
dummyMat = DummyMaterial.Create(shaderMode, shader, gpuInstanced);
|
|
}
|
|
|
|
if (GetDummyMaterial(shaderMode) == null)
|
|
{
|
|
Debug.LogErrorFormat(this, "No dummy material referenced to VLB config for ShaderMode {0}, please try to reset this asset.", shaderMode);
|
|
}
|
|
|
|
RefreshGlobalShaderProperties();
|
|
}
|
|
|
|
static void DeleteAsset<T>(ref T assetObject) where T : UnityEngine.Object
|
|
{
|
|
if (assetObject)
|
|
{
|
|
var path = UnityEditor.AssetDatabase.GetAssetPath(assetObject);
|
|
AssetDatabase.DeleteAsset(path);
|
|
assetObject = null;
|
|
}
|
|
}
|
|
|
|
public static void CleanGeneratedAssets()
|
|
{
|
|
var instance = Instance;
|
|
if (instance)
|
|
{
|
|
DeleteAsset(ref instance._DummyMaterial);
|
|
DeleteAsset(ref instance._DummyMaterialHD);
|
|
DeleteAsset(ref instance._BeamShader);
|
|
DeleteAsset(ref instance._BeamShaderHD);
|
|
DeleteAsset(ref instance);
|
|
}
|
|
}
|
|
#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;
|
|
jitteringNoiseTexture = Resources.Load("VLBBlueNoise", typeof(Texture2D)) as Texture2D;
|
|
|
|
#if UNITY_EDITOR
|
|
RefreshShaders(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()
|
|
{
|
|
CreateDefaultRaymarchingQualityPreset(onlyIfNeeded:true);
|
|
|
|
HandleBackwardCompatibility(pluginVersion, Version.Current);
|
|
pluginVersion = Version.Current;
|
|
}
|
|
|
|
void HandleBackwardCompatibility(int serializedVersion, int newVersion)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (serializedVersion == -1) return; // freshly new spawned config: nothing to do
|
|
if (serializedVersion == newVersion) return; // same version: nothing to do
|
|
|
|
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 (serializedVersion < 1980)
|
|
{
|
|
useLightColorTemperature = false; // light temperature support introduced in version 1980
|
|
EditorUtility.SetDirty(this); // make sure to save this property change
|
|
}
|
|
|
|
if (serializedVersion < 20000)
|
|
{
|
|
ResetInternalData(); // retrieve Jittering Noise texture introduced in 20000
|
|
EditorUtility.SetDirty(this); // make sure to save this property change
|
|
}
|
|
|
|
if (serializedVersion < 20002)
|
|
{
|
|
SetScriptingDefineSymbolsForCurrentRenderPipeline(); // new Scripting Define Symbols introduced in 20002
|
|
}
|
|
|
|
if (newVersion > serializedVersion)
|
|
{
|
|
// Import to keep, we have to regenerate the shader each time the plugin is updated
|
|
RefreshShaders(RefreshShaderFlags.All);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Singleton management
|
|
static Config ms_Instance = null;
|
|
public static Config Instance { get { return GetInstance(true); } }
|
|
|
|
#if UNITY_EDITOR && VLB_DEBUG
|
|
public struct Guard : System.IDisposable {
|
|
public Guard(bool assert) {
|
|
if (m_IsAccessing && assert) Debug.LogError("Circular loop in Config.Instance");
|
|
m_IsAccessing = true;
|
|
}
|
|
|
|
public void Dispose() { m_IsAccessing = false; }
|
|
static bool m_IsAccessing = false;
|
|
}
|
|
#endif // UNITY_EDITOR && VLB_DEBUG
|
|
|
|
#if UNITY_EDITOR
|
|
static bool ms_ShouldInvalidateCache = false;
|
|
public Config()
|
|
{
|
|
ms_ShouldInvalidateCache = true; // new instance detected, force the cache to be refreshed
|
|
}
|
|
#endif
|
|
|
|
static Config LoadAssetInternal(string assetName)
|
|
{
|
|
#if PROFILE_INSTANCE_LOADING
|
|
var startTime = EditorApplication.timeSinceStartup;
|
|
#endif
|
|
var instance = Resources.Load<Config>(assetName);
|
|
#if PROFILE_INSTANCE_LOADING
|
|
var totalTime = EditorApplication.timeSinceStartup - startTime;
|
|
Debug.Log($"Loading {assetName} in {(int)(totalTime*1000)} ms");
|
|
#endif
|
|
return instance;
|
|
}
|
|
|
|
private static Config GetInstance(bool assertIfNotFound)
|
|
{
|
|
#if UNITY_EDITOR && VLB_DEBUG
|
|
using (new Guard(true))
|
|
#endif // UNITY_EDITOR && VLB_DEBUG
|
|
{
|
|
bool updateInstance = ms_Instance == null;
|
|
#if UNITY_EDITOR
|
|
updateInstance |= ms_ShouldInvalidateCache; // Force instance reloading when detecting Config asset changes
|
|
#endif
|
|
if (updateInstance)
|
|
{
|
|
#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 // UNITY_EDITOR
|
|
|
|
// Try to load the instance
|
|
{
|
|
var newInstance = LoadAssetInternal(kAssetName + PlatformHelper.GetCurrentPlatformSuffix());
|
|
if (newInstance == null) newInstance = LoadAssetInternal(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
|
|
}
|
|
ms_ShouldInvalidateCache = false;
|
|
#endif // UNITY_EDITOR
|
|
|
|
ms_Instance = newInstance;
|
|
}
|
|
|
|
if (ms_Instance == null)
|
|
{
|
|
#if UNITY_EDITOR
|
|
ms_IsCreatingInstance = true;
|
|
ms_Instance = CreateInstanceAsset();
|
|
ms_IsCreatingInstance = false;
|
|
|
|
ms_Instance.AutoSelectRenderPipeline();
|
|
ms_Instance.SetScriptingDefineSymbolsForCurrentRenderPipeline(); // force set define symbols the first time we create the instance
|
|
|
|
if (Application.isPlaying)
|
|
ms_Instance.Reset(); // Reset is not automatically when instancing a ScriptableObject when in playmode
|
|
#endif // UNITY_EDITOR
|
|
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;
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
static bool ms_IsCreatingInstance = false;
|
|
|
|
public bool IsCurrentlyUsedInstance() { return Instance == this; }
|
|
|
|
public bool HasValidAssetName()
|
|
{
|
|
if (name.IndexOf(kAssetName) != 0)
|
|
return false;
|
|
|
|
return PlatformHelper.IsValidPlatformSuffix(GetAssetSuffix());
|
|
}
|
|
|
|
public string GetAssetSuffix()
|
|
{
|
|
var fullname = name;
|
|
var strToFind = kAssetName;
|
|
if (fullname.IndexOf(strToFind) == 0) return fullname.Substring(strToFind.Length);
|
|
else return "";
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
static Config CreateInstanceAsset()
|
|
{
|
|
var asset = CreateInstance<Config>();
|
|
Debug.Assert(asset != null);
|
|
CreateFolderAndAsset(asset, "Assets", "Resources", kAssetName + kAssetNameExt);
|
|
return asset;
|
|
}
|
|
|
|
public string GetDebugInfo()
|
|
{
|
|
#if UNITY_2021_2_OR_NEWER
|
|
string scriptingDefineSymbols = PlayerSettings.GetScriptingDefineSymbols(UnityEditor.Build.NamedBuildTarget.FromBuildTargetGroup(EditorUserBuildSettings.selectedBuildTargetGroup));
|
|
#else
|
|
string scriptingDefineSymbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
|
|
#endif
|
|
|
|
return "Unity version: " + Application.unityVersion
|
|
+ "\nVLB version: " + Version.Current
|
|
+ "\nPlatform: " + Application.platform
|
|
+ "\nOS: " + SystemInfo.operatingSystem
|
|
+ "\nShader Level: " + SystemInfo.graphicsShaderLevel
|
|
+ "\nGraphics API: " + SystemInfo.graphicsDeviceType
|
|
+ "\nUses Reversed ZBuffer: " + SystemInfo.usesReversedZBuffer
|
|
+ "\nScripting Define Symbols: " + scriptingDefineSymbols
|
|
+ "\nRender Pipeline Asset: " + (UnityEngine.Rendering.GraphicsSettings.defaultRenderPipeline != null ? UnityEngine.Rendering.GraphicsSettings.defaultRenderPipeline.ToString() : "none")
|
|
+ "\nRender Pipeline Enum: " + SRPHelper.projectRenderPipeline
|
|
+ "\nRender Pipeline Selected: " + renderPipeline
|
|
+ "\nRender Pipeline Symbol: " + SRPHelper.renderPipelineScriptingDefineSymbolAsString
|
|
+ "\nRendering Mode SD: " + GetActualRenderingMode(ShaderMode.SD)
|
|
+ "\nRendering Mode HD: " + GetActualRenderingMode(ShaderMode.HD)
|
|
+ "\nRendering Path: " + (Camera.main != null ? Camera.main.actualRenderingPath.ToString() : "no main camera")
|
|
+ "\nColor Space: " + QualitySettings.activeColorSpace
|
|
;
|
|
}
|
|
#endif // UNITY_EDITOR
|
|
}
|
|
}
|