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