Files
5.24BuYu/Assets/MyGame/Framework/Editor/AnimMapBaker.cs
T
2026-10-20 18:30:03 +08:00

212 lines
5.3 KiB
C#

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
}