#if UNITY_EDITOR using System.Collections.Generic; using UnityEditor; using UnityEditor.Callbacks; using System.Linq; #if SHAPES_URP using UnityEditor.Rendering.Universal; #if UNITY_2021_2_OR_NEWER using URP_RND_DATA_EDITOR = UnityEditor.Rendering.Universal.UniversalRendererDataEditor; #else using URP_RND_DATA_EDITOR = UnityEditor.Rendering.Universal.ForwardRendererDataEditor; #endif #endif #endif using UnityEngine; using UnityEngine.Rendering; #if SHAPES_URP using System.Reflection; using UnityEngine.Rendering.Universal; #if UNITY_2021_2_OR_NEWER using URP_RND_DATA = UnityEngine.Rendering.Universal.UniversalRendererData; #else using URP_RND_DATA = UnityEngine.Rendering.Universal.ForwardRendererData; #endif #endif // Shapes © Freya Holmér - https://twitter.com/FreyaHolmer/ // Website & Documentation - https://acegikmo.com/shapes/ namespace Shapes { public class ShapesImportState : ScriptableObject { [Header( "Do not edit this~" )] [SerializeField] internal RenderPipeline currentShaderRP = RenderPipeline.Legacy; static ShapesImportState instance; public static ShapesImportState Instance => instance != null ? instance : instance = Resources.Load( "Shapes Import State" ); #if UNITY_EDITOR [DidReloadScripts( 1 )] public static void CheckRenderPipelineSoon() { if( ShapesConfig.Instance != null && ShapesConfig.Instance.autoConfigureRenderPipeline ) EditorApplication.delayCall += AutoCheckRenderPipeline; } static void AutoCheckRenderPipeline() { RenderPipeline rpInUnity = UnityInfo.GetCurrentRenderPipelineInUse(); ShapesImportState inst = Instance; if( inst == null ) { Debug.LogWarning( "Failed to get import state - Shapes will retry on the next script recompile" ); return; // I guess some weird import order shenan happened? :c } // make sure we have a valid RP state bool valid = true; string error = ""; RenderPipeline rpShaders = Instance.currentShaderRP; if( rpInUnity != rpShaders ) { valid = false; error += $" • Shape's shaders are compiled for {rpShaders.PrettyName()}\n"; } if( TryGetPreprocessorRP( out RenderPipeline rpPreproc ) ) { if( rpInUnity != rpPreproc ) { valid = false; error += $" • The project keywords are set up for {rpPreproc.PrettyName()}\n"; } } else { valid = false; error += $" • The project keywords are incorrectly set to both HDRP and URP\n"; } if( valid == false ) { string desc = $"Shapes detected a mismatch in render pipeline state.\n" + $"It looks like you are using {rpInUnity.PrettyName()}, but:\n{error}" + $"Would you like to recompile Shapes for your render pipeline?\n(Shapes may not work if you don't)\n\n" + $"Note: You disable this auto-checker in the Shapes settings"; if( EditorUtility.DisplayDialog( "Render pipeline mismatch", desc, $"Recompile for {rpInUnity.PrettyName()}", "cancel" ) ) { ForceSetRP( rpInUnity ); } } } internal static void ForceSetRP( RenderPipeline targetRP ) { Debug.Log( $"Shapes is recompiling for {targetRP.PrettyName()}..." ); ForceSetRpFirstPass( targetRP ); EditorApplication.delayCall += () => { ForceSetRpSecondPass( targetRP ); Debug.Log( $"Shapes is done recompiling" ); }; } static void ForceSetRpFirstPass( RenderPipeline targetRP ) { // set up preprocessor defines, this will also require a second pass of this whole method SetPreprocessorRpSymbols( targetRP ); } static void ForceSetRpSecondPass( RenderPipeline targetRP ) { // makes sure all shaders are compiled to a specific render pipeline RenderPipeline rpShapesShaders = Instance.currentShaderRP; if( rpShapesShaders != targetRP ) CodegenShaders.GenerateShadersAndMaterials( targetRP ); if( targetRP == RenderPipeline.URP ) { string msg = "In order for immediate mode drawing to work, URP render data needs the shapes render features. Would you like to open Shapes settings to make sure immediate mode drawing is supported?"; if( EditorUtility.DisplayDialog( "URP render features", msg, "yes", "no, I don't need IM drawing" ) ) MenuItems.OpenCsharpSettings(); } // also on second pass MakeSureSampleMaterialsAreValid(); } static void MakeSureSampleMaterialsAreValid() { #if SHAPES_URP || SHAPES_HDRP #if UNITY_2019_1_OR_NEWER Shader targetShader = GraphicsSettings.renderPipelineAsset.defaultShader; #else Shader targetShader = GraphicsSettings.renderPipelineAsset.GetDefaultShader(); #endif #else Shader targetShader = UIAssets.Instance.birpDefaultShader; #endif bool changed = false; if( ShapesIO.TryMakeAssetsEditable( UIAssets.Instance.sampleMaterials ) ) { // ensures version control allows us to edit foreach( var mat in UIAssets.Instance.sampleMaterials ) { if( mat == null ) continue; // samples were probably not imported into this project (or they were deleted) if this is null if( mat.shader != targetShader ) { Undo.RecordObject( mat, "Shapes update sample materials shaders" ); Color color = GetMainColor( mat ); mat.shader = targetShader; #if SHAPES_URP || SHAPES_HDRP mat.SetColor( ShapesMaterialUtils.propBaseColor, color ); #else mat.SetColor( ShapesMaterialUtils.propColor, color ); #endif changed = true; } } } if( changed ) Debug.Log( "Shapes updated sample material shaders to match your current render pipeline" ); } static Color GetMainColor( Material mat ) { if( mat.HasProperty( ShapesMaterialUtils.propColor ) ) return mat.GetColor( ShapesMaterialUtils.propColor ); if( mat.HasProperty( ShapesMaterialUtils.propBaseColor ) ) return mat.GetColor( ShapesMaterialUtils.propBaseColor ); return Color.white; } #if SHAPES_URP /* this is pretty cursed, I'm commenting this out for now. static class UrpRndFuncs { const BindingFlags bfs = BindingFlags.Instance | BindingFlags.NonPublic; public static readonly FieldInfo fRndDataList = typeof(UniversalRenderPipelineAsset).GetField( "m_RendererDataList", bfs ); public static readonly MethodInfo fAddComponent = typeof(ScriptableRendererDataEditor).GetMethod( "AddComponent", bfs ); public static readonly MethodInfo fOnEnable = typeof(ScriptableRendererDataEditor).GetMethod( "OnEnable", bfs ); public static readonly bool successfullyLoaded = fRndDataList != null && fAddComponent != null && fOnEnable != null; public static readonly string failMessage = $"Unity's URP API seems to have changed. Failed to load: " + $"{( fRndDataList == null ? "UniversalRenderPipelineAsset.m_RendererDataList" : "" )} " + $"{( fAddComponent == null ? "ScriptableRendererDataEditor.AddComponent" : "" )} " + $"{( fOnEnable == null ? "ScriptableRendererDataEditor.OnEnable" : "" )}"; } static void EnsureShapesPassExistsInTheUrpRenderer() { if( UrpRndFuncs.successfullyLoaded ) { // if our reflected members failed to load, we're kinda screwed :c if( GraphicsSettings.renderPipelineAsset is UniversalRenderPipelineAsset urpa ) { // find the URP asset ScriptableRendererData[] srd = (ScriptableRendererData[])UrpRndFuncs.fRndDataList.GetValue( urpa ); foreach( var rndd in srd.Where( x => x is URP_RND_DATA ) ) { // only add to forward renderer if( rndd.rendererFeatures.Any( x => x is ShapesRenderFeature ) == false ) { // does it have Shapes? // does not contain the Shapes render feature, so, oh boy, here we go~ if( ShapesIO.TryMakeAssetsEditable( urpa ) ) { URP_RND_DATA_EDITOR fwEditor = (URP_RND_DATA_EDITOR)Editor.CreateEditor( rndd ); UrpRndFuncs.fOnEnable.Invoke( fwEditor, null ); // you ever just call OnEnable manually UrpRndFuncs.fAddComponent.Invoke( fwEditor, new[] { (object)nameof(ShapesRenderFeature) } ); DestroyImmediate( fwEditor ); // luv 2 create temporary editors Debug.Log( $"Added Shapes renderer feature to {rndd.name}", rndd ); } } } } else Debug.LogWarning( $"Shapes failed to load the URP pipeline asset to add the renderer feature. " + $"You might have to add {nameof(ShapesRenderFeature)} to your renderer asset manually" ); } else Debug.LogError( UrpRndFuncs.failMessage ); }*/ #endif public static List GetCurrentKeywords() => PlayerSettings.GetScriptingDefineSymbolsForGroup( EditorUserBuildSettings.selectedBuildTargetGroup ).Split( ';' ).ToList(); static void SetCurrentKeywords( IEnumerable keywords ) => PlayerSettings.SetScriptingDefineSymbolsForGroup( EditorUserBuildSettings.selectedBuildTargetGroup, string.Join( ";", keywords ) ); internal static bool TryGetPreprocessorRP( out RenderPipeline rp ) { List keywords = GetCurrentKeywords(); bool kwURP = keywords.Contains( RenderPipeline.URP.PreprocessorDefineName() ); bool kwHDRP = keywords.Contains( RenderPipeline.HDRP.PreprocessorDefineName() ); rp = default; if( kwURP && !kwHDRP ) rp = RenderPipeline.URP; else if( kwHDRP && !kwURP ) rp = RenderPipeline.HDRP; else if( !kwHDRP && !kwURP ) rp = RenderPipeline.Legacy; else return false; return true; } static void SetPreprocessorRpSymbols( RenderPipeline rpTarget ) { Debug.Log( $"Setting preprocessor symbols for {rpTarget.PrettyName()}" ); List symbols = GetCurrentKeywords(); bool changed = false; void CheckRpSymbol( RenderPipeline rp ) { bool on = rp == rpTarget; string ppName = rp.PreprocessorDefineName(); if( on && symbols.Contains( ppName ) == false ) { symbols.Add( ppName ); changed = true; } else if( on == false && symbols.Remove( ppName ) ) changed = true; } CheckRpSymbol( RenderPipeline.URP ); CheckRpSymbol( RenderPipeline.HDRP ); if( changed && ShapesIO.TryMakeAssetsEditable( ShapesIO.projectSettingsPath ) ) { //Debug.Log( $"Shapes updated your project scripting define symbols since you seem to be using {rpTarget.PrettyName()}, I hope that's okay~" ); SetCurrentKeywords( symbols ); } } #endif } }