JJBB/Assets/Editor/Scripts/BuiltinAssetBuilder.cs

491 lines
16 KiB
C#
Raw Permalink Normal View History

2024-08-23 15:49:34 +08:00
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml.Serialization;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Object = UnityEngine.Object;
// 暂时不考虑完全替换内置资源
// 仅仅替换内置Shader防止Shader.Parse
// 其实是替换一些Material和Texture套组
public static class BuiltinAssetBuilder
{
public const string replacementPath = "Assets/BuiltinAssets";
public const string configPath = replacementPath + "/config.txt";
private const string _texturePath = replacementPath + "/Texture";
private const string _materialPath = replacementPath + "/Material";
private const string _shaderPath = replacementPath + "/Shader";
public static BuiltinFileRecord CopyMaterial(Material material)
{
var instance = new Material(material);
var assetPath = _materialPath.Open(material.name);
assetPath = ChangeExtension(assetPath, ".mat");
BuildFilePath(assetPath);
AssetDatabase.Refresh();
AssetDatabase.CreateAsset(instance, assetPath);
return new BuiltinFileRecord(material, instance);
}
public static BuiltinFileRecord CopySprite(Sprite sprite)
{
var importer = SaveTextureToPath(sprite.texture);
importer.textureType = TextureImporterType.Sprite;
importer.SaveAndReimport();
return new BuiltinFileRecord(sprite, AssetDatabase.LoadAssetAtPath<Sprite>(importer.assetPath));
}
public static BuiltinFileRecord CopyTexture2D(Texture2D texture)
{
var importer = SaveTextureToPath(texture);
importer.textureType = TextureImporterType.Default;
importer.SaveAndReimport();
return new BuiltinFileRecord(texture, AssetDatabase.LoadAssetAtPath<Texture2D>(importer.assetPath));
}
public static BuiltinFileRecord GetShaderReplace(List<Shader> replaceList, Shader shader)
{
BuiltinFileRecord record = null;
for (var i = 0; i < replaceList.Count; i++)
{
var replace = replaceList[i];
if (replace.name == shader.name)
{
record = new BuiltinFileRecord(shader, replace);
break;
}
}
return record;
}
private static TextureImporter SaveTextureToPath(Texture2D source)
{
var renderTex = RenderTexture.GetTemporary(source.width, source.height);
Graphics.Blit(source, renderTex);
var texture = new Texture2D(renderTex.width, renderTex.height, TextureFormat.RGBA32, false);
RenderTexture.active = renderTex;
texture.ReadPixels(new Rect(0, 0, renderTex.width, renderTex.height), 0, 0);
RenderTexture.active = null;
RenderTexture.ReleaseTemporary(renderTex);
var assetPath = _texturePath.Open(source.name);
assetPath = ChangeExtension(assetPath, ".png");
var filePath = BuildFilePath(assetPath);
var binary = texture.EncodeToPNG();
Object.DestroyImmediate(texture);
File.WriteAllBytes(filePath, binary);
AssetDatabase.Refresh();
var importer = AssetImporter.GetAtPath(assetPath) as TextureImporter;
return importer;
}
public static void ExtractBuiltinAssets()
{
var spriteList = new List<Sprite>();
var materialList = new List<Material>();
var textureList = new List<Texture2D>();
var shaderList = new List<Shader>();
var unityAssets = AssetDatabase.LoadAllAssetsAtPath("Resources/unity_builtin_extra");
for (var i = 0; i < unityAssets.Length; i++)
{
var asset = unityAssets[i];
var sprite = asset as Sprite;
if (sprite)
{
spriteList.Add(sprite);
continue;
}
var material = asset as Material;
if (material)
{
materialList.Add(material);
continue;
}
var texture = asset as Texture2D;
if (texture)
{
textureList.Add(texture);
continue;
}
var shader = asset as Shader;
if (shader)
{
shaderList.Add(shader);
}
}
for (var i = textureList.Count - 1; i >= 0; i--)
{
var texture = textureList[i];
for (var j = 0; j < spriteList.Count; j++)
if (texture.name == spriteList[j].name)
{
textureList.RemoveAt(i);
break;
}
}
var shaderReplaceList = new List<Shader>();
var shaderReplacePaths = from assetPath in AssetDatabase.GetAllAssetPaths()
where assetPath.StartsWith(_shaderPath)
where Path.GetExtension(assetPath) == ".shader"
select assetPath;
foreach (var assetPath in shaderReplacePaths)
{
var shader = AssetDatabase.LoadAssetAtPath<Shader>(assetPath);
if (shader)
shaderReplaceList.Add(shader);
}
var recordList = new List<BuiltinFileRecord>();
for (var i = 0; i < spriteList.Count; i++)
recordList.Add(CopySprite(spriteList[i]));
for (var i = 0; i < materialList.Count; i++)
recordList.Add(CopyMaterial(materialList[i]));
for (var i = 0; i < textureList.Count; i++)
recordList.Add(CopyTexture2D(textureList[i]));
for (var i = 0; i < shaderList.Count; i++)
{
var record = GetShaderReplace(shaderReplaceList, shaderList[i]);
if (record != null)
recordList.Add(record);
}
var serializer = new XmlSerializer(typeof(List<BuiltinFileRecord>));
var configFilePath = EditorCommonUtility.AssetToFilePath(configPath);
using (var fs = File.Create(configFilePath))
{
serializer.Serialize(fs, recordList);
}
UpdateBuiltinMaterial(recordList);
}
public static void ReplaceForAssetPaths(IList<string> assetPaths)
{
var config = LoadConfig();
if (config != null)
{
for (var i = 0; i < assetPaths.Count; i++)
{
EditorUtility.DisplayProgressBar("替换内置资源中", string.Format("{0} / {1}", i, assetPaths.Count), (float)i / assetPaths.Count);
try
{
var assetPath = assetPaths[i];
var assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
// Scene在执行分解的流程中进行替换
// if (assetType == typeof(SceneAsset))
// {
// var scene = EditorSceneManager.OpenScene(assetPath);
// ReplaceForOneScene(scene, config);
// }
// else
if (assetType == typeof(GameObject))
{
var extension = Path.GetExtension(assetPath);
if (extension == ".prefab")
ReplaceForOneGameObject(AssetDatabase.LoadAssetAtPath<GameObject>(assetPath), config);
}
else if (assetType == typeof(Material))
ReplaceForOneMaterial(AssetDatabase.LoadAssetAtPath<Material>(assetPath), config);
}
catch (Exception e)
{
Debug.LogError(e.ToString());
}
}
Debug.LogWarning("Replace Builtin Assets Finish!");
EditorUtility.ClearProgressBar();
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}
public static void ReplaceForPath(string pathHeader)
{
var assetPaths = (from assetPath in AssetDatabase.GetAllAssetPaths()
where assetPath.StartsWith(pathHeader)
where !assetPath.StartsWith("Assets/StreamingAssets")
select assetPath).ToArray();
ReplaceForAssetPaths(assetPaths);
}
private static string BuildFilePath(string assetPath)
{
var filePath = EditorCommonUtility.AssetToFilePath(assetPath);
var directory = Path.GetDirectoryName(filePath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
Directory.CreateDirectory(directory);
return filePath;
}
private static string ChangeExtension(string assetPath, string extension)
{
var oldExtension = Path.GetExtension(assetPath);
if (!string.IsNullOrEmpty(oldExtension))
assetPath = assetPath.Remove(assetPath.Length - oldExtension.Length);
return assetPath + extension;
}
private static void UpdateBuiltinMaterial(IList<BuiltinFileRecord> config)
{
var materialPaths = from assetPath in AssetDatabase.GetAllAssetPaths()
where assetPath.StartsWith(_materialPath)
where Path.GetExtension(assetPath) == ".mat"
select assetPath;
foreach (var materialPath in materialPaths)
{
var material = AssetDatabase.LoadAssetAtPath<Material>(materialPath);
if (material != null)
ReplaceForOneMaterial(material, config);
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
private static bool ReplaceOneRenderer(Renderer component, IList<BuiltinFileRecord> config)
{
var dirty = false;
var materials = component.sharedMaterials;
for (var i = 0; i < materials.Length; i++)
{
if (materials[i] != null)
{
var replace = GetReplaceAsset(materials[i], config);
if (replace != null)
{
dirty = true;
materials[i] = replace;
}
}
}
if (dirty)
component.sharedMaterials = materials;
return dirty;
}
private static bool ReplaceOneImage(Image image, IList<BuiltinFileRecord> config)
{
var dirty = ReplaceOneGraphic(image, config);
if (image.sprite != null)
{
var replace = GetReplaceAsset(image.sprite, config);
if (replace != null)
{
dirty = true;
image.sprite = replace;
}
}
return dirty;
}
private static bool ReplaceOneRawImage(RawImage rawImage, IList<BuiltinFileRecord> config)
{
var dirty = ReplaceOneGraphic(rawImage, config);
if (rawImage.texture != null)
{
var replace = GetReplaceAsset(rawImage.texture, config);
if (replace != null)
{
dirty = true;
rawImage.texture = replace;
}
}
return dirty;
}
private static bool ReplaceOneGraphic(Graphic graphic, IList<BuiltinFileRecord> config)
{
var dirty = false;
if (graphic.material != null)
{
var replace = GetReplaceAsset(graphic.material, config);
if (replace != null)
{
dirty = true;
graphic.material = replace;
}
}
return dirty;
}
private static void ReplaceForOneMaterial(Material source, IList<BuiltinFileRecord> config)
{
var dirty = false;
var shader = source.shader;
var newShader = Shader.Find(shader.name);
if (shader != newShader)
{
dirty = true;
source.shader = newShader;
}
var count = ShaderUtil.GetPropertyCount(source.shader);
for (var i = 0; i < count; i++)
if (ShaderUtil.GetPropertyType(source.shader, i) == ShaderUtil.ShaderPropertyType.TexEnv)
{
var propertyName = ShaderUtil.GetPropertyName(source.shader, i);
var texture = source.GetTexture(propertyName);
if (texture != null)
{
var texturePath = GetReplacePath(texture, config);
if (!string.IsNullOrEmpty(texturePath))
{
var replace = AssetDatabase.LoadAssetAtPath<Texture2D>(texturePath);
if (replace != null)
{
dirty = true;
source.SetTexture(propertyName, replace);
}
}
}
}
if (dirty)
EditorUtility.SetDirty(source);
}
private static void ReplaceForOneGameObject(GameObject prefab, IList<BuiltinFileRecord> config)
{
var dirty = false;
var renderers = prefab.GetComponentsInChildren<Renderer>(true);
for (var i = 0; i < renderers.Length; i++)
if (ReplaceOneRenderer(renderers[i], config))
dirty = true;
var graphics = prefab.GetComponentsInChildren<Graphic>(true);
for (var i = 0; i < graphics.Length; i++)
{
var image = graphics[i] as Image;
if (image)
{
if (ReplaceOneImage(image, config))
dirty = true;
continue;
}
var rawImage = graphics[i] as RawImage;
if (rawImage)
{
if (ReplaceOneRawImage(rawImage, config))
dirty = true;
continue;
}
if (ReplaceOneGraphic(graphics[i], config))
dirty = true;
}
if (dirty)
EditorUtility.SetDirty(prefab);
}
public static void ReplaceForOneScene(Scene scene)
{
var config = LoadConfig();
if (config != null)
{
var rootObjs = scene.GetRootGameObjects();
for (var i = 0; i < rootObjs.Length; i++)
ReplaceForOneGameObject(rootObjs[i], config);
}
}
private static T GetReplaceAsset<T>(T source, IList<BuiltinFileRecord> config) where T : Object
{
T result = null;
var replacePath = GetReplacePath(source, config);
if (!string.IsNullOrEmpty(replacePath))
result = AssetDatabase.LoadAssetAtPath<T>(replacePath);
return result;
}
private static string GetReplacePath(Object source, IList<BuiltinFileRecord> config)
{
var typeString = source.GetType().ToString();
var fileId = BuiltinFileRecord.GetFileId(source);
BuiltinFileRecord record = null;
for (var i = 0; i < config.Count; i++)
if (config[i].assetType == typeString && config[i].sourceId == fileId)
{
record = config[i];
break;
}
return record == null ? string.Empty : record.replacePath;
}
private static List<BuiltinFileRecord> LoadConfig()
{
List<BuiltinFileRecord> result = null;
var filePath = EditorCommonUtility.AssetToFilePath(configPath);
if (File.Exists(filePath))
{
var serializer = new XmlSerializer(typeof(List<BuiltinFileRecord>));
using (var fs = File.OpenRead(filePath))
{
result = serializer.Deserialize(fs) as List<BuiltinFileRecord>;
}
if (result == null)
Debug.LogError("Builtin Assets Config 反序列化失败!");
}
else
Debug.LogError("Builtin Assets Config 文件未建立!");
return result;
}
}
[Serializable]
public class BuiltinFileRecord
{
private static readonly PropertyInfo _inspectorMode =
typeof(SerializedObject).GetProperty("inspectorMode", BindingFlags.NonPublic | BindingFlags.Instance);
public string assetType;
public string name;
public string replacePath;
public long sourceId;
// 反序列化接口
public BuiltinFileRecord()
{
}
public BuiltinFileRecord(Object oldAsset, Object replaceAsset)
{
name = oldAsset.name;
assetType = oldAsset.GetType().ToString();
sourceId = GetFileId(oldAsset);
replacePath = AssetDatabase.GetAssetPath(replaceAsset);
}
public static long GetFileId(Object asset)
{
var serializedObject = new SerializedObject(asset);
_inspectorMode.SetValue(serializedObject, InspectorMode.Debug, null);
var localIdProp = serializedObject.FindProperty("m_LocalIdentfierInFile");
return localIdProp.longValue;
}
}