com.alicizax.unity.tuyoogam.../Editor/EditorExtension/ShaderVariantCollector/ShaderVariantCollector.cs
2025-09-02 19:21:49 +08:00

251 lines
8.3 KiB
C#
Raw Permalink 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.Diagnostics;
using System.Linq;
using System.IO;
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using YooAsset.Editor;
public static class ShaderVariantCollector
{
private enum ESteps
{
None,
Prepare,
CollectAllMaterial,
CollectVariants,
CollectSleeping,
WaitingDone,
}
private const float WaitMilliseconds = 3000f;
private const float SleepMilliseconds = 3000f;
private static string _savePath;
private static string _packageName;
private static int _processMaxNum;
private static Action _completedCallback;
private static ESteps _steps = ESteps.None;
private static Stopwatch _elapsedTime;
private static List<string> _allMaterials;
private static List<GameObject> _allSpheres = new List<GameObject>(1000);
/// <summary>
/// 开始收集
/// </summary>
public static void Run(string savePath, string packageName, int processMaxNum, Action completedCallback)
{
if (_steps != ESteps.None)
return;
if (Path.HasExtension(savePath) == false)
savePath = $"{savePath}.shadervariants";
if (Path.GetExtension(savePath) != ".shadervariants")
throw new System.Exception("Shader variant file extension is invalid.");
if (string.IsNullOrEmpty(packageName))
throw new System.Exception("Package name is null or empty !");
// 注意先删除再保存否则ShaderVariantCollection内容将无法及时刷新
AssetDatabase.DeleteAsset(savePath);
EditorTools.CreateFileDirectory(savePath);
_savePath = savePath;
_packageName = packageName;
_processMaxNum = processMaxNum;
_completedCallback = completedCallback;
// 聚焦到游戏窗口
EditorTools.FocusUnityGameWindow();
// 创建临时测试场景
CreateTempScene();
_steps = ESteps.Prepare;
EditorApplication.update += EditorUpdate;
}
private static void EditorUpdate()
{
if (_steps == ESteps.None)
return;
if (_steps == ESteps.Prepare)
{
ShaderVariantCollectionHelper.ClearCurrentShaderVariantCollection();
_steps = ESteps.CollectAllMaterial;
return; //等待一帧
}
if (_steps == ESteps.CollectAllMaterial)
{
_allMaterials = GetAllMaterials();
_steps = ESteps.CollectVariants;
return; //等待一帧
}
if (_steps == ESteps.CollectVariants)
{
int count = Mathf.Min(_processMaxNum, _allMaterials.Count);
List<string> range = _allMaterials.GetRange(0, count);
_allMaterials.RemoveRange(0, count);
CollectVariants(range);
if (_allMaterials.Count > 0)
{
_elapsedTime = Stopwatch.StartNew();
_steps = ESteps.CollectSleeping;
}
else
{
_elapsedTime = Stopwatch.StartNew();
_steps = ESteps.WaitingDone;
}
}
if (_steps == ESteps.CollectSleeping)
{
if (_elapsedTime.ElapsedMilliseconds > SleepMilliseconds)
{
DestroyAllSpheres();
_elapsedTime.Stop();
_steps = ESteps.CollectVariants;
}
}
if (_steps == ESteps.WaitingDone)
{
// 注意:一定要延迟保存才会起效
if (_elapsedTime.ElapsedMilliseconds > WaitMilliseconds)
{
_elapsedTime.Stop();
_steps = ESteps.None;
// 保存结果并创建清单
ShaderVariantCollectionHelper.SaveCurrentShaderVariantCollection(_savePath);
CreateManifest();
UnityEngine.Debug.Log($"搜集SVC完毕");
EditorApplication.update -= EditorUpdate;
_completedCallback?.Invoke();
}
}
}
private static void CreateTempScene()
{
EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects);
}
private static List<string> GetAllMaterials()
{
// 获取所有打包的资源
CollectResult collectResult = AssetBundleCollectorSettingData.Setting.BeginCollect(_packageName, false, false);
// 搜集所有材质球
int progressValue = 0;
HashSet<string> result = new HashSet<string>();
foreach (var collectAssetInfo in collectResult.CollectAssets)
{
if (collectAssetInfo.AssetInfo.AssetType == typeof(UnityEngine.Material))
{
string assetPath = collectAssetInfo.AssetInfo.AssetPath;
if (result.Contains(assetPath) == false)
result.Add(assetPath);
}
foreach(var dependAssetInfo in collectAssetInfo.DependAssets)
{
if (dependAssetInfo.AssetType == typeof(UnityEngine.Material))
{
string assetPath = dependAssetInfo.AssetPath;
if (result.Contains(assetPath) == false)
result.Add(assetPath);
}
}
EditorTools.DisplayProgressBar("搜集所有材质球", ++progressValue, collectResult.CollectAssets.Count);
}
EditorTools.ClearProgressBar();
// 返回结果
return result.ToList();
}
private static void CollectVariants(List<string> materials)
{
Camera camera = Camera.main;
if (camera == null)
throw new System.Exception("Not found main camera.");
// 设置主相机
float aspect = camera.aspect;
int totalMaterials = materials.Count;
float height = Mathf.Sqrt(totalMaterials / aspect) + 1;
float width = Mathf.Sqrt(totalMaterials / aspect) * aspect + 1;
float halfHeight = Mathf.CeilToInt(height / 2f);
float halfWidth = Mathf.CeilToInt(width / 2f);
camera.orthographic = true;
camera.orthographicSize = halfHeight;
camera.transform.position = new Vector3(0f, 0f, -10f);
// 创建测试球体
int xMax = (int)(width - 1);
int x = 0, y = 0;
int progressValue = 0;
for (int i = 0; i < materials.Count; i++)
{
var material = materials[i];
var position = new Vector3(x - halfWidth + 1f, y - halfHeight + 1f, 0f);
var go = CreateSphere(material, position, i);
if (go != null)
_allSpheres.Add(go);
if (x == xMax)
{
x = 0;
y++;
}
else
{
x++;
}
EditorTools.DisplayProgressBar("照射所有材质球", ++progressValue, materials.Count);
}
EditorTools.ClearProgressBar();
}
private static GameObject CreateSphere(string assetPath, Vector3 position, int index)
{
var material = AssetDatabase.LoadAssetAtPath<Material>(assetPath);
var shader = material.shader;
if (shader == null)
return null;
var go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
go.GetComponent<Renderer>().sharedMaterial = material;
go.transform.position = position;
go.name = $"Sphere_{index} | {material.name}";
return go;
}
private static void DestroyAllSpheres()
{
foreach (var go in _allSpheres)
{
GameObject.DestroyImmediate(go);
}
_allSpheres.Clear();
// 尝试释放编辑器加载的资源
EditorUtility.UnloadUnusedAssetsImmediate(true);
}
private static void CreateManifest()
{
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
ShaderVariantCollection svc = AssetDatabase.LoadAssetAtPath<ShaderVariantCollection>(_savePath);
if (svc != null)
{
var wrapper = ShaderVariantCollectionManifest.Extract(svc);
string jsonData = JsonUtility.ToJson(wrapper, true);
string savePath = _savePath.Replace(".shadervariants", ".json");
File.WriteAllText(savePath, jsonData);
}
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
}
}