public void AddFrames(string nameLocal, List <Vector3[]> pFrames, MeshAnimationBoneGroup pBoneGroup) { Name = nameLocal; FrameCount = pFrames.Count; if (pFrames.Count < 1) { return; } FrameSize = pFrames[0].Length; List <float> list = new List <float>(); foreach (Vector3[] current in pFrames) { Vector3[] array = current; for (int i = 0; i < array.Length; i++) { Vector3 vector = array[i]; list.Add(vector.x); list.Add(vector.y); list.Add(vector.z); } } FloatFrames = list.ToArray(); list.Clear(); BoneNames = pBoneGroup.BoneNames.ToArray(); foreach (KeyValuePair <string, MeshAnimationBoneTransform> current2 in pBoneGroup.Bones) { foreach (Vector3 current3 in current2.Value.Positions) { list.Add(current3.x); list.Add(current3.y); list.Add(current3.z); } } FloatBonePositions = list.ToArray(); list.Clear(); foreach (KeyValuePair <string, MeshAnimationBoneTransform> current4 in pBoneGroup.Bones) { foreach (Quaternion current5 in current4.Value.Rotations) { list.Add(current5.x); list.Add(current5.y); list.Add(current5.z); list.Add(current5.w); } } FloatBoneRotations = list.ToArray(); list.Clear(); }
public static void ExportCombinedTexture(GameObject pFbxInstance, ExportParameters pSettings, System.Action <float> pProgressBarUpdater = null) { if (!AreParametersValid(pFbxInstance, pSettings)) { return; } //List<AnimationClip> exportClips = GetClips (); //List<Transform> exportBoneTransforms = GetBoneTransforms (); //List<string> exportBoneNames = GetBoneNames (); AnimationClip[] exportClips = pSettings.animationClips; Transform[] exportBoneTransforms = pSettings.boneTransforms; string[] exportBoneNames = pSettings.boneNames; string[] animationNames = pSettings.animationNames; GameObject model = pFbxInstance; Transform baseTransform = model.transform; Animation animation = model.GetComponentInChildren <Animation>(); ///origin code //SkinnedMeshRenderer renderer = model.GetComponentInChildren<SkinnedMeshRenderer>(); //Mesh mesh = renderer.sharedMesh; SkinnedMeshRenderer[] renderArray = model.GetComponentsInChildren <SkinnedMeshRenderer>(); int arrayLength = renderArray.Length; Mesh[] meshArray = new Mesh[arrayLength]; for (int i = 0; i < arrayLength; i++) { meshArray[i] = renderArray[i].sharedMesh; } float frameInterval = 1.0f / pSettings.framerate; //清空并重建目标目录 if (!System.IO.Directory.Exists(pSettings.outputFilePath)) { System.IO.Directory.CreateDirectory(pSettings.outputFilePath); } string filePath = pSettings.outputFilePath + model.name + ".asset"; if (File.Exists(filePath)) { File.Delete(filePath); } ///modify by niexin ///Attack1","Win","Dead","Hit","Run","Skill1","Wait1","Wait2" 8 default animations ///first line marked as attached model info, first color to number,following by vertex counts for each sub mesh,which is the width of texture ///某行首位标记attach数量信息,如0则无,那2行则为该物体动画数据,否则往上推,后续像素r分别写入顶点数,即动画纹理宽度 ///8个动画的顺序依次标记首位8个像素,r通道填入是否存在的信息,g,b分别对应初始行,结束行位置, int totalframeCount = 0; totalframeCount = 1 + arrayLength; int aniFrameCount = 0; //pre cacl the length of all frames for (int i = 0; i < exportClips.Length; i++) { AnimationClip clip = exportClips[i]; if (null == clip) { continue; } float clipLength = clip.length; //Mesh frameMesh; //List<Vector3[]> frameVertices = new List<Vector3[]>(); // Get the list of times for each frame List <float> frameTimes = GetFrameTimes(clipLength, frameInterval); aniFrameCount += frameTimes.Count; } totalframeCount += aniFrameCount * arrayLength; int[] vertexCount = new int[arrayLength]; int maxVertexCount = 0; for (int i = 0; i < arrayLength; i++) { int count = meshArray[i].vertexCount; vertexCount[i] = count; if (count > maxVertexCount) { maxVertexCount = count; } } Vector3[][] defaultAnimationInfos = new Vector3[arrayLength][]; for (int i = 0; i < arrayLength; i++) { defaultAnimationInfos[i] = new Vector3[8]; for (int j = 0; j < 8; j++) { defaultAnimationInfos[i][j] = new Vector3(0, 0, 0); } } string[] defaultAnimationArray = new string[] { "Attack1", "Win", "Dead", "Hit", "Run", "Skill1", "Wait1", "Wait2" }; List <string> defaultAnimationList = new List <string>(); for (int i = 0; i < defaultAnimationArray.Length; i++) { defaultAnimationList.Add(defaultAnimationArray[i].ToUpper()); } MeshSkinDataProxy data = MeshSkinDataProxy.CreateInstance <MeshSkinDataProxy>(); data.isLoadAllInMainScene = pSettings.isLoadAllInMainScene; data.arrayLength = arrayLength; data.maxVertexCount = maxVertexCount; data.vertexCounts = vertexCount; data.meshes = new MeshSkinDataProxy.SubMeshData[arrayLength]; Texture2D combinedTex = new Texture2D(maxVertexCount, totalframeCount, TextureFormat.RGBAHalf, false); combinedTex.SetPixel(0, 0, new Color(arrayLength, maxVertexCount, 0)); int countData = 1 + arrayLength; for (int i = 0; i < arrayLength; i++) { combinedTex.SetPixel(i + 1, 0, new Color(vertexCount[i], 0, 0)); countData += meshArray[i].uv.Length / 2 + meshArray[i].triangles.Length / 3; } //////配置纹理 fps uv count, uv, tri count tri int texSize = 64; //不要问我为什么是64 int texH = (int)Mathf.Ceil(countData / (float)texSize); int texW = texSize; Texture2D cfgTexture = new Texture2D(texW, texH, TextureFormat.RGBAHalf, false); Color[] colors = new Color[texW * texH]; int cfgDataIdx = 0; colors[cfgDataIdx++] = new Color(arrayLength, 0, 0); for (int i = 0; i < arrayLength; i++) { colors[cfgDataIdx++] = new Color(pSettings.framerate, meshArray[i].uv.Length, meshArray[i].triangles.Length); } int frame = 1 + arrayLength; for (int submeshCount = 0; submeshCount < arrayLength; submeshCount++) { data.meshes[submeshCount].clips = new MeshSkinDataProxy.ClipData[defaultAnimationArray.Length]; data.meshes[submeshCount].framerate = pSettings.framerate; for (int i = 0; i < exportClips.Length; i++) { if (pProgressBarUpdater != null) { pProgressBarUpdater((float)i / (float)(exportClips.Length + 1)); } MeshAnimationBoneGroup boneGroup = new MeshAnimationBoneGroup(exportBoneNames.ToList(), exportBoneNames.Length); // Set the animation clip to export AnimationClip clip = exportClips[i]; if (null == clip) { continue; } int infoIdx = -1; string clipName = clip.name; if (clipName.Equals("walk")) { clipName = "Hit"; } else if (clipName.Equals("cut")) { clipName = "Skill1"; } else if (clipName.Equals("victory")) { clipName = "Dead"; } string upperName = clipName.ToUpper(); if (defaultAnimationList.Contains(upperName)) { infoIdx = defaultAnimationList.IndexOf(upperName); defaultAnimationInfos[submeshCount][infoIdx].x = 1; } animation.AddClip(clip, clip.name); animation.clip = clip; AnimationState state = animation[clip.name]; state.enabled = true; state.weight = 1; float clipLength = clip.length; //Mesh frameMesh; //List<Vector3[]> frameVertices = new List<Vector3[]>(); // Get the list of times for each frame List <float> frameTimes = GetFrameTimes(clipLength, frameInterval); ////顶点纹理 r->x,g->y,b->z,a->s //sample each sub mesh //start frame position try { defaultAnimationInfos[submeshCount][infoIdx].y = frame; } catch { Debug.LogError(pFbxInstance.name + "," + defaultAnimationInfos.Length + "," + defaultAnimationInfos.LongLength + "," + submeshCount + "," + infoIdx); continue; } data.meshes[submeshCount].clips[infoIdx].frames = new MeshSkinDataProxy.FrameData[frameTimes.Count]; int index = 0; foreach (float time in frameTimes) { state.time = time; animation.Play(); animation.Sample(); // Grab the position and rotation for each bone at the current frame for (int k = 0; k < exportBoneTransforms.Length; k++) { string name = exportBoneNames[k]; Vector3 pos = baseTransform.InverseTransformPoint(exportBoneTransforms[k].position); Quaternion rot = exportBoneTransforms[k].rotation * Quaternion.Inverse(baseTransform.rotation); boneGroup.Bones[name].Positions.Add(pos); boneGroup.Bones[name].Rotations.Add(rot); } Mesh bakeMesh = null; //if (pSettings.quaternionOffset != Quaternion.identity) //{ // Matrix4x4 matrix = new Matrix4x4(); // matrix.SetTRS(Vector2.zero, pSettings.quaternionOffset, Vector3.one); // bakeMesh = BakeFrameAfterMatrixTransform(renderArray[submeshCount], matrix); //} //else //{ // bakeMesh = new Mesh(); // renderArray[submeshCount].BakeMesh(bakeMesh); //} Matrix4x4 matrix = new Matrix4x4(); matrix = renderArray[submeshCount].transform.localToWorldMatrix; bakeMesh = BakeFrameAfterMatrixTransform(renderArray[submeshCount], matrix); data.meshes[submeshCount].clips[infoIdx].frames[index++].vertexs = bakeMesh.vertices; for (int k = 0; k < bakeMesh.vertexCount; k++) { Vector3 vertex = bakeMesh.vertices[k]; combinedTex.SetPixel(k, frame, new Color(vertex.x, vertex.y, vertex.z)); } bakeMesh.Clear(); Object.DestroyImmediate(bakeMesh); frame++; animation.Stop(); } //end frame position,exclude defaultAnimationInfos[submeshCount][infoIdx].z = frame; } data.meshes[submeshCount].uvs = meshArray[submeshCount].uv; data.meshes[submeshCount].triangles = meshArray[submeshCount].triangles; for (int i = 0; i < meshArray[submeshCount].uv.Length / 2; i++) { int uvIdx = i * 2; colors[cfgDataIdx++] = new Color(meshArray[submeshCount].uv[uvIdx].x, meshArray[submeshCount].uv[uvIdx].y, meshArray[submeshCount].uv[uvIdx + 1].x, meshArray[submeshCount].uv[uvIdx + 1].y); } for (int i = 0; i < meshArray[submeshCount].triangles.Length / 3; i++) { int triIdx = i * 3; colors[cfgDataIdx++] = new Color(meshArray[submeshCount].triangles[triIdx], meshArray[submeshCount].triangles[triIdx + 1], meshArray[submeshCount].triangles[triIdx + 2]); } } //group.AddAnimation(animationNames[i], frameVertices, boneGroup); for (int j = 0; j < arrayLength; j++) { for (int i = 0; i < 8; i++) { combinedTex.SetPixel(i, j + 1, new Color(defaultAnimationInfos[j][i].x, defaultAnimationInfos[j][i].y, defaultAnimationInfos[j][i].z)); } } combinedTex.Apply(false); string dataPath = string.Format("{0}{1}.asset", pSettings.outputFilePath, pFbxInstance.name); AssetDatabase.CreateAsset(data, dataPath); if (pProgressBarUpdater != null) { pProgressBarUpdater((float)exportClips.Length / (float)(exportClips.Length + 1)); } AssetDatabase.Refresh(); }
public static void Export(GameObject pFbxInstance, ExportParameters pSettings, System.Action <float> pProgressBarUpdater = null) { if (AreParametersValid(pFbxInstance, pSettings)) { //List<AnimationClip> exportClips = GetClips (); //List<Transform> exportBoneTransforms = GetBoneTransforms (); //List<string> exportBoneNames = GetBoneNames (); AnimationClip[] exportClips = pSettings.animationClips; Transform[] exportBoneTransforms = pSettings.boneTransforms; string[] exportBoneNames = pSettings.boneNames; string[] animationNames = pSettings.animationNames; GameObject model = pFbxInstance; Transform baseTransform = model.transform; SkinnedMeshRenderer renderer = model.GetComponentInChildren <SkinnedMeshRenderer>(); Mesh mesh = renderer.sharedMesh; Animation animation = model.GetComponentInChildren <Animation>(); // Set up the basic MeshAnimationGroupSerializable properties //MeshAnimationGroupSerializable group = new MeshAnimationGroupSerializable(); //group.SetUV(mesh.uv); //group.SetTriangles(mesh.triangles); //group.SetFrameInterval(pSettings.framerate); //group.modelName = pFbxInstance.name; //group.boneCount = exportBoneTransforms.Length; float frameInterval = 1.0f / pSettings.framerate; //Mesh modifiedMesh = GameObject.Instantiate(mesh); //List<Vector4> modifiedTangent = new List<Vector4>(); //for (int mi = 0; mi < mesh.vertexCount; mi++) { // Vector4 tangent = new Vector4(mi, 0, 0); // modifiedTangent.Add(tangent); //} //modifiedMesh.SetTangents(modifiedTangent); //AssetDatabase.CreateAsset(modifiedMesh, "Assets/GpuInstance/ooxx.asset"); //清空并重建目标目录 if (!System.IO.Directory.Exists(pSettings.outputFilePath)) { System.IO.Directory.CreateDirectory(pSettings.outputFilePath); } else { System.IO.Directory.Delete(pSettings.outputFilePath, true); System.IO.Directory.CreateDirectory(pSettings.outputFilePath); } for (int i = 0; i < exportClips.Length; i++) { if (pProgressBarUpdater != null) { pProgressBarUpdater((float)i / (float)(exportClips.Length + 1)); } MeshAnimationBoneGroup boneGroup = new MeshAnimationBoneGroup(exportBoneNames.ToList(), exportBoneNames.Length); // Set the animation clip to export AnimationClip clip = exportClips[i]; if (null == clip) { continue; } animation.AddClip(clip, clip.name); animation.clip = clip; AnimationState state = animation[clip.name]; state.enabled = true; state.weight = 1; float clipLength = clip.length; //Mesh frameMesh; //List<Vector3[]> frameVertices = new List<Vector3[]>(); // Get the list of times for each frame List <float> frameTimes = GetFrameTimes(clipLength, frameInterval); ////顶点纹理 r->x,g->y,b->z,a->s Texture2D vertexTex = new Texture2D(mesh.vertexCount, frameTimes.Count, TextureFormat.RGBAHalf, false); int frame = 0; foreach (float time in frameTimes) { state.time = time; animation.Play(); animation.Sample(); // Grab the position and rotation for each bone at the current frame for (int k = 0; k < exportBoneTransforms.Length; k++) { string name = exportBoneNames[k]; Vector3 pos = baseTransform.InverseTransformPoint(exportBoneTransforms[k].position); Quaternion rot = exportBoneTransforms[k].rotation * Quaternion.Inverse(baseTransform.rotation); boneGroup.Bones[name].Positions.Add(pos); boneGroup.Bones[name].Rotations.Add(rot); } Mesh bakeMesh = null; //if (pSettings.quaternionOffset != Quaternion.identity) { // Matrix4x4 matrix = new Matrix4x4(); // matrix.SetTRS(Vector2.zero, pSettings.quaternionOffset, Vector3.one); // bakeMesh = BakeFrameAfterMatrixTransform(renderer, matrix); //} else { // bakeMesh = new Mesh(); // renderer.BakeMesh(bakeMesh); //} Matrix4x4 matrix = new Matrix4x4(); //matrix.SetTRS(Vector2.zero, pSettings.quaternionOffset, Vector3.one); matrix = renderer.transform.localToWorldMatrix; bakeMesh = BakeFrameAfterMatrixTransform(renderer, matrix); for (int k = 0; k < bakeMesh.vertexCount; k++) { Vector3 vertex = bakeMesh.vertices[k]; vertexTex.SetPixel(k, frame, new Color(vertex.x, vertex.y, vertex.z)); } bakeMesh.Clear(); Object.DestroyImmediate(bakeMesh); frame++; animation.Stop(); } //group.AddAnimation(animationNames[i], frameVertices, boneGroup); vertexTex.Apply(false); string dataPath = pSettings.outputFilePath + "/" + pSettings.animationNames[i] + ".asset"; AssetDatabase.CreateAsset(vertexTex, dataPath); } ////配置纹理 fps uv count, uv, tri count tri int countData = mesh.uv.Length / 2 + mesh.triangles.Length / 3 + 1; int texSize = 64; //不要问我为什么是64 int texH = (int)Mathf.Ceil(countData / (float)texSize); int texW = texSize; Texture2D cfgTexture = new Texture2D(texW, texH, TextureFormat.RGBAHalf, false); Color[] colors = new Color[texW * texH]; int cfgDataIdx = 0; colors[cfgDataIdx++] = new Color(pSettings.framerate, mesh.uv.Length, mesh.triangles.Length); for (int i = 0; i < mesh.uv.Length / 2; i++) { int uvIdx = i * 2; colors[cfgDataIdx++] = new Color(mesh.uv[uvIdx].x, mesh.uv[uvIdx].y, mesh.uv[uvIdx + 1].x, mesh.uv[uvIdx + 1].y); } for (int i = 0; i < mesh.triangles.Length / 3; i++) { int triIdx = i * 3; colors[cfgDataIdx++] = new Color(mesh.triangles[triIdx], mesh.triangles[triIdx + 1], mesh.triangles[triIdx + 2]); } cfgTexture.SetPixels(colors); cfgTexture.Apply(); //Test(cfgTexture); AssetDatabase.CreateAsset(cfgTexture, pSettings.outputFilePath + "/" + pFbxInstance.name + ".asset"); if (pProgressBarUpdater != null) { pProgressBarUpdater((float)exportClips.Length / (float)(exportClips.Length + 1)); } //MeshAnimationProtobufHelper.SerializeObject<MeshAnimationGroupSerializable>(pSettings.outputFilePath + "/" + pFbxInstance.name + ".bytes", group); EditorUtility.DisplayDialog("Tip", "Mesh Animation Export Complete" + pFbxInstance.name, "OK"); AssetDatabase.Refresh(); } }