AllAssets
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// 保存需要烘焙的动画的相关数据
|
||||
/// </summary>
|
||||
public struct AnimData
|
||||
{
|
||||
#region 字段
|
||||
|
||||
public int vertexCount;
|
||||
public int mapWidth;
|
||||
public List<AnimationState> animClips;
|
||||
public string name;
|
||||
|
||||
private Animation animation;
|
||||
private SkinnedMeshRenderer skin;
|
||||
|
||||
#endregion
|
||||
|
||||
public AnimData(Animation anim, SkinnedMeshRenderer smr, string goName)
|
||||
{
|
||||
vertexCount = smr.sharedMesh.vertexCount;
|
||||
mapWidth = Mathf.NextPowerOfTwo(vertexCount);
|
||||
animClips = new List<AnimationState>(anim.Cast<AnimationState>());
|
||||
animation = anim;
|
||||
skin = smr;
|
||||
name = goName;
|
||||
}
|
||||
|
||||
#region 方法
|
||||
|
||||
public void AnimationPlay(string animName)
|
||||
{
|
||||
this.animation.Play(animName);
|
||||
}
|
||||
|
||||
public void SampleAnimAndBakeMesh(ref Mesh m)
|
||||
{
|
||||
this.SampleAnim();
|
||||
this.BakeMesh(ref m);
|
||||
}
|
||||
|
||||
private void SampleAnim()
|
||||
{
|
||||
if (this.animation == null)
|
||||
{
|
||||
Debug.LogError("animation is null!!");
|
||||
return;
|
||||
}
|
||||
|
||||
this.animation.Sample();
|
||||
}
|
||||
|
||||
private void BakeMesh(ref Mesh m)
|
||||
{
|
||||
if (this.skin == null)
|
||||
{
|
||||
Debug.LogError("skin is null!!");
|
||||
return;
|
||||
}
|
||||
|
||||
this.skin.BakeMesh(m);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 烘焙后的数据
|
||||
/// </summary>
|
||||
public struct BakedData
|
||||
{
|
||||
#region 字段
|
||||
|
||||
public string name;
|
||||
public float animLen;
|
||||
public byte[] rawAnimMap;
|
||||
public int animMapWidth;
|
||||
public int animMapHeight;
|
||||
|
||||
#endregion
|
||||
|
||||
public BakedData(string name, float animLen, Texture2D animMap)
|
||||
{
|
||||
this.name = name;
|
||||
this.animLen = animLen;
|
||||
this.animMapHeight = animMap.height;
|
||||
this.animMapWidth = animMap.width;
|
||||
this.rawAnimMap = animMap.GetRawTextureData();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 烘焙器
|
||||
/// </summary>
|
||||
public class AnimMapBaker{
|
||||
|
||||
#region 字段
|
||||
|
||||
private AnimData? animData = null;
|
||||
private List<Vector3> vertices = new List<Vector3>();
|
||||
private Mesh bakedMesh;
|
||||
|
||||
private List<BakedData> bakedDataList = new List<BakedData>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region 方法
|
||||
|
||||
public void SetAnimData(GameObject go)
|
||||
{
|
||||
if(go == null)
|
||||
{
|
||||
Debug.LogError("go is null!!");
|
||||
return;
|
||||
}
|
||||
|
||||
Animation anim = go.GetComponent<Animation>();
|
||||
SkinnedMeshRenderer smr = go.GetComponentInChildren<SkinnedMeshRenderer>();
|
||||
|
||||
if(anim == null || smr == null)
|
||||
{
|
||||
Debug.LogError("anim or smr is null!!");
|
||||
return;
|
||||
}
|
||||
this.bakedMesh = new Mesh();
|
||||
this.animData = new AnimData(anim, smr, go.name);
|
||||
}
|
||||
int animNum;
|
||||
public List<BakedData> Bake()
|
||||
{
|
||||
if(this.animData == null)
|
||||
{
|
||||
Debug.LogError("bake data is null!!");
|
||||
return this.bakedDataList;
|
||||
}
|
||||
//每一个动作都生成一个动作图
|
||||
for(int i = 0; i < this.animData.Value.animClips.Count; i++)
|
||||
{
|
||||
animNum = i;
|
||||
if (!this.animData.Value.animClips[i].clip.legacy)
|
||||
{
|
||||
|
||||
Debug.LogError(string.Format("{0} is not legacy!!", this.animData.Value.animClips[i].clip.name));
|
||||
continue;
|
||||
}
|
||||
|
||||
BakePerAnimClip(this.animData.Value.animClips[i]);
|
||||
}
|
||||
|
||||
return this.bakedDataList;
|
||||
}
|
||||
|
||||
private void BakePerAnimClip(AnimationState curAnim)
|
||||
{
|
||||
int curClipFrame = 0;
|
||||
float sampleTime = 0;
|
||||
float perFrameTime = 0;
|
||||
|
||||
curClipFrame = Mathf.ClosestPowerOfTwo((int)(curAnim.clip.frameRate * curAnim.length));
|
||||
perFrameTime = curAnim.length / curClipFrame; ;
|
||||
|
||||
Texture2D animMap = new Texture2D(this.animData.Value.mapWidth, curClipFrame, TextureFormat.RGBAHalf, false);
|
||||
animMap.name = string.Format("{0}_{1}.animMap", this.animData.Value.name, curAnim.name);
|
||||
this.animData.Value.AnimationPlay(curAnim.name);
|
||||
|
||||
for (int i = 0; i < curClipFrame; i++)
|
||||
{
|
||||
//EditorUtility.DisplayCancelableProgressBar("转换进度", $"正在处理第 {i}/{curClipFrame} 步", (float)i / curClipFrame);
|
||||
curAnim.time = sampleTime;
|
||||
|
||||
this.animData.Value.SampleAnimAndBakeMesh(ref this.bakedMesh);
|
||||
|
||||
for(int j = 0; j < this.bakedMesh.vertexCount; j++)
|
||||
{
|
||||
EditorUtility.DisplayCancelableProgressBar("转换进度", $"正在处理第 {i}/{curClipFrame} 步 {j}/{bakedMesh.vertexCount}点", (float)i / curClipFrame);
|
||||
Vector3 vertex = this.bakedMesh.vertices[j];
|
||||
animMap.SetPixel(j, i, new Color(vertex.x, vertex.y, vertex.z));
|
||||
}
|
||||
|
||||
sampleTime += perFrameTime;
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
|
||||
animMap.Apply();
|
||||
Debug.Log($"{animNum}/{animData.Value.animClips.Count-1}");
|
||||
if (animNum== animData.Value.animClips.Count - 1)
|
||||
{
|
||||
Debug.Log("转换完成");
|
||||
}
|
||||
|
||||
this.bakedDataList.Add(new BakedData(animMap.name, curAnim.clip.length, animMap));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region 属性
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.IO;
|
||||
|
||||
public class AnimMapBakerWindow : EditorWindow {
|
||||
|
||||
private enum SaveStrategy
|
||||
{
|
||||
AnimMap,//only anim map
|
||||
Mat,//with shader
|
||||
Prefab//prefab with mat
|
||||
}
|
||||
|
||||
#region 字段
|
||||
|
||||
public static GameObject targetGo;
|
||||
private static AnimMapBaker baker;
|
||||
private static string path = "GamePlay_ECS\\EntityConvert";
|
||||
|
||||
private static string subPath = "SubPath";
|
||||
private static SaveStrategy stratege = SaveStrategy.AnimMap;
|
||||
private static Shader _animMapShader;
|
||||
|
||||
private static Shader animMapShader
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_animMapShader == null)
|
||||
_animMapShader = Shader.Find(animMapShaderName);
|
||||
return _animMapShader;
|
||||
}
|
||||
}
|
||||
private static string animMapShaderName = "ECSGame/URP/UnlitAnim";
|
||||
#endregion
|
||||
|
||||
|
||||
#region 方法
|
||||
|
||||
[MenuItem("Tools/AnimMapBaker")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
EditorWindow.GetWindow(typeof(AnimMapBakerWindow));
|
||||
baker = new AnimMapBaker();
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
targetGo = (GameObject)EditorGUILayout.ObjectField(targetGo, typeof(GameObject), true);
|
||||
subPath = targetGo == null ? subPath : targetGo.name;
|
||||
EditorGUILayout.LabelField(string.Format("保存路径output path:{0}", Path.Combine(path, subPath)));
|
||||
path = EditorGUILayout.TextField(path);
|
||||
subPath = EditorGUILayout.TextField(subPath);
|
||||
stratege = (SaveStrategy)EditorGUILayout.EnumPopup("保存策略output type:", stratege);
|
||||
if (GUILayout.Button("Bake"))
|
||||
{
|
||||
if(targetGo == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("err", "targetGo is null!", "OK");
|
||||
return;
|
||||
}
|
||||
|
||||
if(baker == null)
|
||||
{
|
||||
baker = new AnimMapBaker();
|
||||
}
|
||||
baker.SetAnimData(targetGo);
|
||||
List<BakedData> list =baker.Bake();
|
||||
if (list != null)
|
||||
{
|
||||
for(int i = 0; i < list.Count; i++)
|
||||
{
|
||||
BakedData data = list[i];
|
||||
Save(ref data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void Save(ref BakedData data)
|
||||
{
|
||||
switch(stratege)
|
||||
{
|
||||
case SaveStrategy.AnimMap:
|
||||
SaveAsAsset(ref data);
|
||||
break;
|
||||
case SaveStrategy.Mat:
|
||||
SaveAsMat(ref data);
|
||||
break;
|
||||
case SaveStrategy.Prefab:
|
||||
SaveAsPrefab(ref data);
|
||||
break;
|
||||
}
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
private Texture2D SaveAsAsset(ref BakedData data)
|
||||
{
|
||||
string folderPath = CreateFolder();
|
||||
Texture2D animMap = new Texture2D(data.animMapWidth, data.animMapHeight, TextureFormat.RGBAHalf, false);
|
||||
animMap.LoadRawTextureData(data.rawAnimMap);
|
||||
AssetDatabase.CreateAsset(animMap, Path.Combine(folderPath, GetValidName(data.name) + ".asset"));
|
||||
return animMap;
|
||||
}
|
||||
|
||||
public static string GetValidName(string pName)
|
||||
{
|
||||
pName = pName.Replace("|", "@");
|
||||
return pName;
|
||||
}
|
||||
|
||||
private Material SaveAsMat(ref BakedData data)
|
||||
{
|
||||
if(animMapShader == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("err", "shader is null!!", "OK");
|
||||
return null;
|
||||
}
|
||||
|
||||
if(targetGo == null || !targetGo.GetComponentInChildren<SkinnedMeshRenderer>())
|
||||
{
|
||||
EditorUtility.DisplayDialog("err", "SkinnedMeshRender is null!!", "OK");
|
||||
return null;
|
||||
}
|
||||
|
||||
SkinnedMeshRenderer smr = targetGo.GetComponentInChildren<SkinnedMeshRenderer>();
|
||||
Material mat = new Material(animMapShader);
|
||||
Texture2D animMap = SaveAsAsset(ref data);
|
||||
mat.SetTexture("_MainTex", smr.sharedMaterial.mainTexture);
|
||||
mat.SetTexture("_AnimMap", animMap);
|
||||
mat.SetFloat("_AnimLen", data.animLen);
|
||||
|
||||
string folderPath = CreateFolder();
|
||||
AssetDatabase.CreateAsset(mat, Path.Combine(folderPath, GetValidName(data.name) + ".mat"));
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
private void SaveAsPrefab(ref BakedData data)
|
||||
{
|
||||
Material mat = SaveAsMat(ref data);
|
||||
|
||||
if(mat == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("err", "mat is null!!", "OK");
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject go = new GameObject();
|
||||
go.AddComponent<MeshRenderer>().sharedMaterial = mat;
|
||||
go.AddComponent<MeshFilter>().sharedMesh = targetGo.GetComponentInChildren<SkinnedMeshRenderer>().sharedMesh;
|
||||
|
||||
string folderPath = CreateFolder();
|
||||
PrefabUtility.CreatePrefab(Path.Combine(folderPath, GetValidName(data.name) + ".prefab").Replace("\\", "/"), go);
|
||||
}
|
||||
|
||||
private string CreateFolder()
|
||||
{
|
||||
string folderPath = Path.Combine("Assets/" + path, subPath);
|
||||
if (!AssetDatabase.IsValidFolder(folderPath))
|
||||
{
|
||||
AssetDatabase.CreateFolder("Assets/" + path, subPath);
|
||||
}
|
||||
return folderPath;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user