using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using System.Linq; using System.IO; using System.Threading.Tasks; /// /// 保存需要烘焙的动画的相关数据 /// public struct AnimData { #region 字段 public int vertexCount; public int mapWidth; public List 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(anim.Cast()); 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 } /// /// 烘焙后的数据 /// 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(); } } /// /// 烘焙器 /// public class AnimMapBaker{ #region 字段 private AnimData? animData = null; private List vertices = new List(); private Mesh bakedMesh; private List bakedDataList = new List(); #endregion #region 方法 public void SetAnimData(GameObject go) { if(go == null) { Debug.LogError("go is null!!"); return; } Animation anim = go.GetComponent(); SkinnedMeshRenderer smr = go.GetComponentInChildren(); 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 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 }