using System; using System.IO; using System.Collections.Generic; using System.Linq; #if UNITY_EDITOR using UnityEditor; #endif namespace UnityEngine.Rendering.HighDefinition { sealed partial class DiffusionProfileSettings : IVersionable { enum Version { Initial, // 16 profiles per asset DiffusionProfileRework, // one profile per asset } [Obsolete("Profiles are obsolete, only one diffusion profile per asset is allowed.")] internal DiffusionProfile this[int index] { get => profile; } [SerializeField] Version m_Version = MigrationDescription.LastVersion(); Version IVersionable.version { get => m_Version; set => m_Version = value; } [Obsolete("Profiles are obsolete, only one diffusion profile per asset is allowed.")] internal DiffusionProfile[] profiles; static readonly MigrationDescription k_Migration = MigrationDescription.New( MigrationStep.New(Version.DiffusionProfileRework, (DiffusionProfileSettings d) => { #pragma warning disable 618 #if UNITY_EDITOR if (d.profiles == null) return; // If the asset importer for the asset we're upgrading is null, it means that the asset // does not exists on the disk and we don't want to upgrade these assets string assetPath = AssetDatabase.GetAssetPath(d); var importer = AssetImporter.GetAtPath(assetPath); if (importer == null) return; var currentHDAsset = HDRenderPipeline.currentAsset; if (currentHDAsset == null) throw new Exception("Can't upgrade diffusion profile when the HDRenderPipeline asset is not assigned in Graphic Settings"); var defaultProfile = new DiffusionProfile(true); // Iterate over the diffusion profile settings and generate one new asset for each // diffusion profile which have been modified, and store them into a dictionary to be able to upgrade materials int index = 0; var newProfiles = new Dictionary(); string assetName = Path.GetFileNameWithoutExtension(assetPath); foreach (var profile in d.profiles) { if (!profile.Equals(defaultProfile)) { newProfiles[index] = CreateNewDiffusionProfile(d, assetName, profile, index); // Update the diffusion profile hash is required for assets that are upgraded because it will // be assigned to materials right after the create of the asset so we don't wait for the auto hash update UnityEditor.Rendering.HighDefinition.DiffusionProfileHashTable.UpdateDiffusionProfileHashNow(newProfiles[index]); } index++; } // We write in the main diffusion profile meta filethe list of created asset so we know where to look // when we upgrade materials inside scenes (from the menu item) SerializableGUIDs toJson; toJson.assetGUIDs = new string[16]; foreach (var kp in newProfiles) toJson.assetGUIDs[kp.Key] = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(kp.Value)); importer.userData = JsonUtility.ToJson(toJson); // Update the diffusion profiles references in all the hd assets where this profile was set var hdAssetsGUIDs = AssetDatabase.FindAssets("t:HDRenderPipelineAsset"); foreach (var hdAssetGUID in hdAssetsGUIDs) { var hdAsset = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(hdAssetGUID)); if (hdAsset.diffusionProfileSettings == d) { // Assign the new diffusion profile assets into the HD asset hdAsset.diffusionProfileSettingsList = new DiffusionProfileSettings[newProfiles.Keys.Max() + 1]; foreach (var kp in newProfiles) hdAsset.diffusionProfileSettingsList[kp.Key] = kp.Value; UnityEditor.EditorUtility.SetDirty(hdAsset); } } // If the diffusion profile settings we're upgrading was assigned to the HDAsset in use // then we need to go over all materials and upgrade them if (currentHDAsset.diffusionProfileSettings == d) { var materialGUIDs = AssetDatabase.FindAssets("t:Material"); foreach (var guid in materialGUIDs) { var mat = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid)); UpgradeMaterial(mat, d); } } #endif #pragma warning restore 618 }) ); #if UNITY_EDITOR public static void UpgradeMaterial(Material mat, DiffusionProfileSettings mainProfile) { UpgradeMaterial(mat, mainProfile, "_DiffusionProfile", "_DiffusionProfileAsset", "_DiffusionProfileHash"); // For layered material: UpgradeMaterial(mat, mainProfile, "_DiffusionProfile0", "_DiffusionProfileAsset0", "_DiffusionProfileHash0"); UpgradeMaterial(mat, mainProfile, "_DiffusionProfile1", "_DiffusionProfileAsset1", "_DiffusionProfileHash1"); UpgradeMaterial(mat, mainProfile, "_DiffusionProfile2", "_DiffusionProfileAsset2", "_DiffusionProfileHash2"); UpgradeMaterial(mat, mainProfile, "_DiffusionProfile3", "_DiffusionProfileAsset3", "_DiffusionProfileHash3"); } [System.Serializable] struct SerializableGUIDs { [SerializeField] public string[] assetGUIDs; } static void UpgradeMaterial(Material mat, DiffusionProfileSettings mainProfile, string diffusionProfile, string diffusionProfileAsset, string diffusionProfileHash) { // if the material don't have a diffusion profile if (!mat.HasProperty(diffusionProfile) || !mat.HasProperty(diffusionProfileAsset) || !mat.HasProperty(diffusionProfileHash)) return; // We can't upgrade materials if the diffusion profile is null if (mainProfile == null) return; // or if it already have been upgraded int index = mat.GetInt(diffusionProfile) - 1; // the index in the material is stored with +1 because 0 is none if (index < 0) return; mat.SetInt(diffusionProfile, -1); var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(mainProfile)); SerializableGUIDs profiles = JsonUtility.FromJson(importer.userData); if (String.IsNullOrEmpty(profiles.assetGUIDs ? [index])) { Debug.LogError("Could not upgrade diffusion profile reference in material " + mat + ": index " + index + " not found in main diffusion profile"); return; } string diffusionProfileGUID = profiles.assetGUIDs[index]; var newProfile = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(diffusionProfileGUID)); mat.SetVector(diffusionProfileAsset, HDUtils.ConvertGUIDToVector4(diffusionProfileGUID)); mat.SetFloat(diffusionProfileHash, HDShadowUtils.Asfloat(newProfile.profile.hash)); if (newProfile.profile.hash == 0) Debug.LogError("Diffusion profile hash of " + newProfile + " have not been initialized !"); if (mat.shader.name.StartsWith("HDRP/")) { // Set the reference value for the stencil test. int stencilRef = (int)StencilUsage.Clear; int stencilWriteMask = (int)StencilUsage.RequiresDeferredLighting | (int)StencilUsage.SubsurfaceScattering; int stencilGBufferRef = (int)StencilUsage.RequiresDeferredLighting; int stencilGBufferMask = (int)StencilUsage.RequiresDeferredLighting | (int)StencilUsage.SubsurfaceScattering; if (mat.HasProperty("_MaterialID") && (int)mat.GetFloat("_MaterialID") == 0) // 0 is MaterialId.LitSSS { stencilRef = (int)StencilUsage.SubsurfaceScattering; stencilGBufferRef |= (int)StencilUsage.SubsurfaceScattering; } if (mat.HasProperty("_ReceivesSSR") && mat.GetInt("_ReceivesSSR") == 1) { stencilWriteMask |= (int)StencilUsage.TraceReflectionRay; stencilRef |= (int)StencilUsage.TraceReflectionRay; stencilGBufferMask |= (int)StencilUsage.TraceReflectionRay; stencilGBufferRef |= (int)StencilUsage.TraceReflectionRay; } // As we tag both during motion vector pass and Gbuffer pass we need a separate state and we need to use the write mask mat.SetInt("_StencilRef", stencilRef); mat.SetInt("_StencilWriteMask", stencilWriteMask); mat.SetInt("_StencilRefGBuffer", stencilGBufferRef); mat.SetInt("_StencilWriteMaskGBuffer", stencilGBufferMask); mat.SetInt("_StencilRefMV", (int)StencilUsage.ObjectMotionVector); mat.SetInt("_StencilWriteMaskMV", (int)StencilUsage.ObjectMotionVector); } } static DiffusionProfileSettings CreateNewDiffusionProfile(DiffusionProfileSettings asset, string assetName, DiffusionProfile profile, int index) { var path = AssetDatabase.GetAssetPath(asset); path = Path.GetDirectoryName(path) + "/" + assetName + Path.GetExtension(path); path = AssetDatabase.GenerateUniqueAssetPath(path); if (index == 0) { asset.profile = profile; profile.Validate(); asset.UpdateCache(); AssetDatabase.MoveAsset(AssetDatabase.GetAssetPath(asset), path); return asset; } var newDiffusionProfile = ScriptableObject.CreateInstance(); newDiffusionProfile.name = asset.name; newDiffusionProfile.profile = profile; newDiffusionProfile.m_Version = Version.DiffusionProfileRework; profile.Validate(); newDiffusionProfile.UpdateCache(); AssetDatabase.CreateAsset(newDiffusionProfile, path); return newDiffusionProfile; } public void TryToUpgrade() { if (k_Migration.Migrate(this)) { UnityEditor.EditorUtility.SetDirty(this); UnityEditor.AssetDatabase.SaveAssets(); // Do not refresh the database now because it will force the reimport of all new diffusion profile settings EditorApplication.delayCall += UnityEditor.AssetDatabase.Refresh; } } #endif } }