Files
Fishing2/Packages/com.waveharmonic.crest/Editor/Scripts/MaterialUpgrader.cs
2026-01-31 00:32:49 +08:00

239 lines
7.3 KiB
C#

// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEditor;
using UnityEngine;
using WaveHarmonic.Crest.Editor.Settings;
namespace WaveHarmonic.Crest.Editor
{
static partial class MaterialUpgrader
{
[InitializeOnLoadMethod]
static void OnLoad()
{
EditorApplication.update += OnEditorUpdate;
}
static void OnEditorUpdate()
{
if (Time.renderedFrameCount <= 0)
{
return;
}
if (EditorApplication.isUpdating)
{
return;
}
if (ProjectSettings.Instance._MaterialVersion > 0)
{
EditorApplication.update -= OnEditorUpdate;
return;
}
UpgradeMaterials(force: false);
EditorApplication.update -= OnEditorUpdate;
}
[MenuItem("Edit/Crest/Materials/Upgrade Materials")]
static void OnMenuSelect()
{
UpgradeMaterials(force: true);
}
static void UpgradeMaterials(bool force)
{
var dirty = false;
var version = force ? 0 : ProjectSettings.Instance._MaterialVersion;
AssetDatabase.StartAssetEditing();
try
{
foreach (var guid in AssetDatabase.FindAssets("t:Material"))
{
var material = AssetDatabase.LoadAssetAtPath<Material>(AssetDatabase.GUIDToAssetPath(guid));
dirty |= UpgradeMaterial(material, version);
}
ProjectSettings.Instance._MaterialVersion = k_MaterialVersion;
ProjectSettings.Save();
if (dirty)
{
AssetDatabase.SaveAssets();
}
}
finally
{
AssetDatabase.StopAssetEditing();
}
}
}
static partial class MaterialUpgrader
{
enum SerializedType
{
Boolean,
Integer,
Float,
Vector,
Color,
Texture,
Keyword,
}
static bool TryFindBase(SerializedObject material, SerializedType type, out SerializedProperty propertyBase)
{
propertyBase = material.FindProperty("m_SavedProperties");
switch (type)
{
case SerializedType.Integer:
propertyBase = propertyBase.FindPropertyRelative("m_Ints");
return true;
case SerializedType.Boolean:
case SerializedType.Float:
propertyBase = propertyBase.FindPropertyRelative("m_Floats");
return true;
case SerializedType.Color:
case SerializedType.Vector:
propertyBase = propertyBase.FindPropertyRelative("m_Colors");
return true;
case SerializedType.Texture:
propertyBase = propertyBase.FindPropertyRelative("m_TexEnvs");
return true;
}
return false;
}
static SerializedProperty FindBase(SerializedObject material, SerializedType type)
{
if (!TryFindBase(material, type, out var root))
{
throw new System.ArgumentException($"Unknown SerializedType {type}");
}
return root;
}
static bool TryFindProperty(SerializedObject material, string name, SerializedType type, out SerializedProperty property, out int index, SerializedProperty root)
{
var isKeyword = type == SerializedType.Keyword;
property = null;
var size = root.arraySize;
for (index = 0; index < size; ++index)
{
property = root.GetArrayElementAtIndex(index);
if (isKeyword ? property.stringValue == name : property.FindPropertyRelative("first").stringValue == name)
{
break;
}
}
if (index == size)
{
return false;
}
if (!isKeyword)
{
property = property.FindPropertyRelative("second");
}
return true;
}
static bool TryFindProperty(SerializedObject material, string name, SerializedType type, out SerializedProperty property, out int index, out SerializedProperty root)
{
if (type == SerializedType.Keyword)
{
root = material.FindProperty("m_ValidKeywords");
if (TryFindProperty(material, name, type, out property, out index, root))
{
return true;
}
root = material.FindProperty("m_InvalidKeywords");
if (TryFindProperty(material, name, type, out property, out index, root))
{
return true;
}
return false;
}
root = FindBase(material, type);
return TryFindProperty(material, name, type, out property, out index, root);
}
static bool RenameFloat(SerializedObject so, Material material, string old, string @new)
{
if (!TryFindProperty(so, old, SerializedType.Float, out var oldProperty, out var oldIndex, out var parent))
{
return false;
}
var oldValue = oldProperty.floatValue;
parent.DeleteArrayElementAtIndex(oldIndex);
parent.InsertArrayElementAtIndex(0);
var newProperty = parent.GetArrayElementAtIndex(0);
newProperty.FindPropertyRelative("first").stringValue = @new;
newProperty.FindPropertyRelative("second").floatValue = oldValue;
return true;
}
static bool RenameKeyword(SerializedObject so, Material material, string old, string @new)
{
if (!TryFindProperty(so, old, SerializedType.Keyword, out var oldProperty, out var oldIndex, out var parent))
{
return false;
}
parent.DeleteArrayElementAtIndex(oldIndex);
parent.InsertArrayElementAtIndex(0);
var keyword = parent.GetArrayElementAtIndex(0);
keyword.stringValue = @new;
return true;
}
}
// Upgrades
static partial class MaterialUpgrader
{
public const int k_MaterialVersion = 1;
static bool UpgradeMaterial(Material material, int version)
{
var so = new SerializedObject(material);
var dirty = false;
// Upgrade materials.
// Version is for all materials.
if (version < 1)
{
switch (material.shader.name)
{
case "Crest/Water":
dirty |= RenameKeyword(so, material, "CREST_FLOW_ON", "_CREST_FLOW_LOD");
dirty |= RenameFloat(so, material, "CREST_FLOW", "_CREST_FLOW_LOD");
break;
}
}
if (dirty)
{
so.ApplyModifiedPropertiesWithoutUndo();
EditorUtility.SetDirty(material);
}
return dirty;
}
}
}