491 lines
16 KiB
C#
491 lines
16 KiB
C#
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;
|
||
}
|
||
} |