private void CaptureSnapShot(AnimationClip animClip, float bakeFrames, GPUAnimation meshAnim, GameObject sampleGO) { List <GPUAnimationFrameData> verts = new List <GPUAnimationFrameData>(); List <Vector3> meshesInFrame = new List <Vector3>(); float lastFrameTime = 0; for (int i = 0; i <= bakeFrames; i += frameSkips[animClip.name]) { float bakeDelta = Mathf.Clamp01(((float)i / bakeFrames)); EditorUtility.DisplayProgressBar("Baking Animation", string.Format("Processing: {0} Frame: {1}", animClip.name, i), bakeDelta); float animationTime = bakeDelta * animClip.length; if (requiresAnimator) { float normalizedTime = animationTime / animClip.length; animator.Play(animClip.name, 0, normalizedTime); if (lastFrameTime == 0) { float nextBakeDelta = Mathf.Clamp01(((float)(i += frameSkips[animClip.name]) / bakeFrames)); float nextAnimationTime = nextBakeDelta * animClip.length; lastFrameTime = animationTime - nextAnimationTime; } animator.Update(animationTime - lastFrameTime); lastFrameTime = animationTime; } else { GameObject sampleObject = sampleGO; Animation legacyAnimation = sampleObject.GetComponentInChildren <Animation>(); if (animator && animator.gameObject != sampleObject) { sampleObject = animator.gameObject; } else if (legacyAnimation && legacyAnimation.gameObject != sampleObject) { sampleObject = legacyAnimation.gameObject; } animClip.SampleAnimation(sampleObject, animationTime); } meshesInFrame.Clear(); Mesh m = null; SkinnedMeshRenderer sampleSr = sampleGO.GetComponentInChildren <SkinnedMeshRenderer>(); m = new Mesh(); sampleSr.BakeMesh(m); Vector3[] v = m.vertices; for (int vIndex = 0; vIndex < v.Length; vIndex++) { v[vIndex] = sampleSr.transform.TransformPoint(v[vIndex]); } meshesInFrame.AddRange(v); DestroyImmediate(m); GPUAnimationFrameData data = new GPUAnimationFrameData(); data.SetVerts(meshesInFrame.ToArray()); verts.Add(data); } meshAnim.verts = verts.ToArray(); }
private void CreateSnapshots() { UnityEditor.Animations.AnimatorController bakeController = null; string assetPath = GetPrefabPath(); if (string.IsNullOrEmpty(assetPath)) { EditorUtility.DisplayDialog("Mesh Animator", "Unable to locate the asset path for prefab: " + prefab.name, "OK"); return; } HashSet <string> allAssets = new HashSet <string>(); List <AnimationClip> clips = GetClips(); foreach (var clip in clips) { allAssets.Add(AssetDatabase.GetAssetPath(clip)); } string[] split = assetPath.Split("/".ToCharArray()); string assetFolder = string.Empty; for (int s = 0; s < split.Length - 1; s++) { assetFolder += split[s] + "/"; } var sampleGO = GameObject.Instantiate(prefab, Vector3.zero, Quaternion.identity) as GameObject; if (meshFilters.Count(q => q) == 0 && skinnedRenderers.Count(q => q) == 0) { throw new System.Exception("Bake Error! No MeshFilter's or SkinnedMeshRender's found to bake!"); } else { animator = sampleGO.GetComponent <Animator>(); if (animator == null) { animator = sampleGO.GetComponentInChildren <Animator>(); } InitAnimationBones(sampleGO, null); if (requiresAnimator) { bakeController = CreateBakeController(); if (animator == null) { animator = sampleGO.AddComponent <Animator>(); animator.runtimeAnimatorController = bakeController; animator.avatar = GetAvatar(); } else { animator.runtimeAnimatorController = bakeController; animator.avatar = GetAvatar(); } animator.cullingMode = AnimatorCullingMode.AlwaysAnimate; animator.applyRootMotion = rootMotionMode == GPUAnimation.RootMotionMode.Baked; } GPUAnimationList meshAnimationList = ScriptableObject.CreateInstance <GPUAnimationList>(); meshAnimationList.meshAnimations = new GPUAnimation[clips.Count]; int startIndex = bonesMap.Count * 3; Transform rootMotionBaker = new GameObject().transform; for (int x = 0; x < clips.Count; x++) { AnimationClip animClip = clips[x]; InitAnimationClipHashPath(animClip); if (bakeAnims.ContainsKey(animClip.name) && bakeAnims[animClip.name] == false) { continue; } if (frameSkips.ContainsKey(animClip.name) == false) { Debug.LogWarningFormat("No animation with name {0} in frame skips", animClip.name); continue; } string meshAnimationPath = string.Format("{0}{1}.asset", assetFolder, FormatClipName(animClip.name)); GPUAnimation meshAnim = new GPUAnimation(); meshAnim.length = animClip.length; int bakeFrames = Mathf.CeilToInt(animClip.length * fps); meshAnim.animationName = animClip.name; meshAnim.startIndex = startIndex; meshAnim.clipJoints = new Skeleton[bonesMap.Count]; startIndex += bonesMap.Count * bakeFrames * 3; for (int i = 0; i < bonesMap.Count; i++) { meshAnim.clipJoints[i] = new Skeleton(); meshAnim.clipJoints[i].jontFrameMatrixs = new Matrix4x4[bakeFrames]; } if (animClip.isLooping) { meshAnim.wrapMode = WrapMode.Loop; } else { meshAnim.wrapMode = animClip.wrapMode; } meshAnim.frameSkip = frameSkips[animClip.name]; meshAnimationList.meshAnimations[x] = meshAnim; } Color[] meshTexturePixels = null; Texture2D meshTexture = null; int textureSize = 2; int pixelIndex = 0; if (bakeVertices == false) { while (textureSize * textureSize < startIndex) { textureSize = textureSize << 1; } meshTexture = new Texture2D(textureSize, textureSize, TextureFormat.RGBAHalf, false, true); meshTexture.filterMode = FilterMode.Point; meshTexturePixels = meshTexture.GetPixels(); Matrix4x4 matrix = Matrix4x4.identity; for (int i = 0; i < bonesMap.Count; i++) { meshTexturePixels[pixelIndex] = new Color(matrix.m00, matrix.m01, matrix.m02, matrix.m03); pixelIndex++; meshTexturePixels[pixelIndex] = new Color(matrix.m10, matrix.m11, matrix.m12, matrix.m13); pixelIndex++; meshTexturePixels[pixelIndex] = new Color(matrix.m20, matrix.m21, matrix.m22, matrix.m23); pixelIndex++; } } int animCount = 0; for (int j = 0; j < clips.Count; j++) { AnimationClip animClip = clips[j]; GPUAnimation meshAnim = meshAnimationList.meshAnimations[j]; int bakeFrames = Mathf.CeilToInt(animClip.length * fps); meshAnim.totalFrames = bakeFrames; int frame = 0; for (int i = 0; i <= bakeFrames; i += frameSkips[animClip.name]) { if (bakeVertices) { CaptureSnapShot(animClip, bakeFrames, meshAnim, sampleGO); } else { float bakeDelta = Mathf.Clamp01(((float)i / bakeFrames)); EditorUtility.DisplayProgressBar("Baking Animation", string.Format("Processing: {0} Frame: {1}", animClip.name, i), bakeDelta); CaptureSnapShot(animClip, bakeFrames, bakeDelta, meshAnim, meshTexturePixels, ref pixelIndex); } frame++; } meshAnim.meshTexturePixels = meshTexturePixels; animCount++; } if (bakeVertices) { for (int i = 0; i < meshAnimationList.meshAnimations.Length; i++) { GPUAnimation animation = meshAnimationList.meshAnimations[i]; animation.isVertsAnimation = true; int smallTextureSize = 0; int totalCountPixels = 0; for (int j = 0; j < animation.verts.Length; j++) { totalCountPixels += animation.verts[j].decompressed.Length; } CalculateTextureSize(ref smallTextureSize, totalCountPixels); Texture2D meshSmallTexture = new Texture2D(smallTextureSize, smallTextureSize, TextureFormat.RGBAHalf, false, true); animation.textureSize = smallTextureSize; Color[] meshSmallTextureData = meshSmallTexture.GetPixels(); meshSmallTexture.filterMode = FilterMode.Point; int meshSmallTextureDataIndex = 0; for (int k = 0; k < animation.verts.Length; k++) { GPUAnimationFrameData animationFrameData = animation.verts[k]; animation.totalVerts = animationFrameData.decompressed.Length; for (int n = 0; n < animationFrameData.decompressed.Length; n++) { Vector3 vert = animationFrameData.decompressed[n]; meshSmallTextureData[meshSmallTextureDataIndex] = new Color(vert.x, vert.y, vert.z); meshSmallTextureDataIndex++; //meshSmallTexture.SetPixel(n, k, new Color(vert.x, vert.y, vert.z)); } } meshSmallTexture.SetPixels(meshSmallTextureData); meshSmallTexture.Apply(); AssetDatabase.CreateAsset(meshSmallTexture, assetFolder + "_VertsAnimationTexture.asset"); } AssetDatabase.CreateAsset(meshAnimationList, assetFolder + "_VertsAnimationConfig.asset"); } else { meshTexture.SetPixels(meshTexturePixels); meshTexture.Apply(); AssetDatabase.CreateAsset(meshTexture, assetFolder + "_AnimationTexture.asset"); meshAnimationList.totalJoints = bonesMap.Count; meshAnimationList.skinningTexSize = textureSize; AssetDatabase.CreateAsset(meshAnimationList, assetFolder + "_AnimationConfig.asset"); SkinnedMeshRenderer skinnedMeshRenderer = sampleGO.GetComponentInChildren <SkinnedMeshRenderer>(); Mesh sharedMesh = skinnedMeshRenderer.sharedMesh; Transform[] transforms = skinnedMeshRenderer.bones; int vertexCount = sharedMesh.vertexCount; List <Vector4> indices = new List <Vector4>(); List <Vector4> weights = new List <Vector4>(); Vector3[] vertices = new Vector3[vertexCount]; Vector3[] normals = new Vector3[vertexCount]; Matrix4x4 meshMatrix = skeletonPoses[bonesMap[transforms[0].name]] * sharedMesh.bindposes[0]; BoneWeight[] boneWeights = sharedMesh.boneWeights; for (int v = 0; v < vertexCount; v++) { BoneWeight weight = boneWeights[v]; float weight0 = weight.weight0; float weight1 = weight.weight1; float weight2 = weight.weight2; float weight3 = weight.weight3; int boneIndex0 = bonesMap[transforms[weight.boneIndex0].name]; int boneIndex1 = bonesMap[transforms[weight.boneIndex1].name]; int boneIndex2 = bonesMap[transforms[weight.boneIndex2].name]; int boneIndex3 = bonesMap[transforms[weight.boneIndex3].name]; indices.Add(new Vector4(boneIndex0, boneIndex1, boneIndex2, boneIndex3)); weights.Add(new Vector4(weight0, weight1, weight2, weight3)); vertices[v] = meshMatrix * sharedMesh.vertices[v]; normals[v] = meshMatrix * sharedMesh.normals[v]; weight.boneIndex0 = boneIndex0; weight.boneIndex1 = boneIndex1; weight.boneIndex2 = boneIndex2; weight.boneIndex3 = boneIndex3; boneWeights[v] = weight; } Mesh newMesh = new Mesh(); newMesh.vertices = vertices; newMesh.normals = normals; newMesh.triangles = sharedMesh.triangles; newMesh.uv = sharedMesh.uv; newMesh.SetUVs(1, indices); newMesh.SetUVs(2, weights); skinnedMeshRenderer.bones = joints; skinnedMeshRenderer.sharedMesh = newMesh; AssetDatabase.CreateAsset(newMesh, assetFolder + "_" + sharedMesh.name + ".asset"); } } GameObject.DestroyImmediate(sampleGO); EditorUtility.ClearProgressBar(); EditorUtility.DisplayDialog("GPU Animator", string.Format("Baked {0} animation{1} successfully!", clips.Count , clips.Count > 1 ? "s" : string.Empty), "OK"); AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(bakeController)); }