JJBB/Assets/Editor/Scripts/BuiltinAssetBuilder.cs
2024-08-23 15:49:34 +08:00

491 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
}