// Copyright (c) 2024 Synty Studios Limited. All rights reserved. // // Use of this software is subject to the terms and conditions of the Synty Studios End User Licence Agreement (EULA) // available at: https://syntystore.com/pages/end-user-licence-agreement // // For additional details, see the LICENSE.MD file bundled with this software. using Synty.SidekickCharacters.SkinnedMesh; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace Synty.SidekickCharacters.Utils { public static class BlendShapeUtils { /// /// Collects all of the blend shape data from a given skinnedMesh and Mesh, and stores them as blend shape data. /// /// The mesh to get the blend shape data from. /// The skinned mesh to get the blend shape data from. /// The blend shape names, or partial names to exclude from the data. /// The start position for the delta vertices index. /// Passed in blendshape data used /// A list of BlendShapeData from all blend shape data on the mesh and skinned mesh. public static List GetBlendShapeData( Mesh mesh, SkinnedMeshRenderer skinnedMesh, string[] excludedBlendNames, int verticesCountStartIndex, List allBlendShapeData ) { // Debug.Log("Blend count in getter: " + allBlendShapeData.Count); int totalVerticesVerifiedAtHereForBlendShapes = verticesCountStartIndex; string[] blendShapes = new string[skinnedMesh.sharedMesh.blendShapeCount]; for (int i = 0; i < skinnedMesh.sharedMesh.blendShapeCount; i++) { string blendShapeName = skinnedMesh.sharedMesh.GetBlendShapeName(i); if (excludedBlendNames.All(ebn => !blendShapeName.Contains(ebn))) { blendShapes[i] = blendShapeName; } } for (int i = 0; i < blendShapes.Length; i++) { if (blendShapes[i] == null) { continue; } string bsn = blendShapes[i]; int index = bsn.IndexOf('.') + 1; bsn = "MESHBlends." + bsn.Substring(index, bsn.Length - index); int blendIndex = skinnedMesh.sharedMesh.GetBlendShapeIndex(blendShapes[i]); BlendShapeData blendShapeData = new BlendShapeData { blendShapeFrameName = bsn, blendShapeFrameIndex = blendIndex }; if (allBlendShapeData.Count > 0 && allBlendShapeData.Contains(blendShapeData)) { BlendShapeData existingData = allBlendShapeData.Find(data => data.Equals(blendShapeData)); if (existingData != null) { blendShapeData = existingData; allBlendShapeData.Remove(blendShapeData); } } blendShapeData.blendShapeCurrentValue = skinnedMesh.GetBlendShapeWeight(blendIndex); Mesh sharedMesh = skinnedMesh.sharedMesh; int framesCount = sharedMesh.GetBlendShapeFrameCount(blendIndex); Vector3[] originalDeltaVertices = new Vector3[sharedMesh.vertexCount]; Vector3[] originalDeltaNormals = new Vector3[sharedMesh.vertexCount]; Vector3[] originalDeltaTangents = new Vector3[sharedMesh.vertexCount]; Vector3[] finalDeltaVertices = new Vector3[mesh.vertexCount]; Vector3[] finalDeltaNormals = new Vector3[mesh.vertexCount]; Vector3[] finalDeltaTangents = new Vector3[mesh.vertexCount]; if (blendShapeData.startDeltaVertices.Count < 1) { blendShapeData.startDeltaVertices.AddRange(finalDeltaVertices); blendShapeData.startDeltaNormals.AddRange(finalDeltaNormals); blendShapeData.startDeltaTangents.AddRange(finalDeltaTangents); blendShapeData.finalDeltaVertices.AddRange(finalDeltaVertices); blendShapeData.finalDeltaNormals.AddRange(finalDeltaNormals); blendShapeData.finalDeltaTangents.AddRange(finalDeltaTangents); } if (skinnedMesh.sharedMesh.GetBlendShapeIndex(blendShapes[i]) != -1) { skinnedMesh.sharedMesh.GetBlendShapeFrameVertices( blendIndex, framesCount - 1, originalDeltaVertices, originalDeltaNormals, originalDeltaTangents ); } for (int x = 0; x < originalDeltaVertices.Length; x++) { blendShapeData.finalDeltaVertices[x + totalVerticesVerifiedAtHereForBlendShapes] = originalDeltaVertices[x]; } for (int x = 0; x < originalDeltaNormals.Length; x++) { blendShapeData.finalDeltaNormals[x + totalVerticesVerifiedAtHereForBlendShapes] = originalDeltaNormals[x]; } for (int x = 0; x < originalDeltaTangents.Length; x++) { blendShapeData.finalDeltaTangents[x + totalVerticesVerifiedAtHereForBlendShapes] = originalDeltaTangents[x]; } allBlendShapeData.Add(blendShapeData); } return allBlendShapeData; } /// /// Restores the given blend shape data to the given mesh and skinned mesh. /// /// The blend shape data to restore. /// The mesh to restore the blend shape data to. /// The mesh renderer that the mesh belongs to. public static void RestoreBlendShapeData(List blendData, Mesh meshToRestoreTo, SkinnedMeshRenderer meshRenderer) { Dictionary alreadyAddedBlendShapesNames = new Dictionary(); foreach (BlendShapeData blendShape in blendData) { string blendShapeName = blendShape.blendShapeFrameName; if (alreadyAddedBlendShapesNames.TryGetValue(blendShape.blendShapeFrameName, out int name)) { blendShapeName += " (" + name + ")"; } meshToRestoreTo.AddBlendShapeFrame( blendShapeName, 0.0f, blendShape.startDeltaVertices.ToArray(), blendShape.startDeltaNormals.ToArray(), blendShape.startDeltaTangents.ToArray() ); meshToRestoreTo.AddBlendShapeFrame( blendShapeName, 100.0f, blendShape.finalDeltaVertices.ToArray(), blendShape.finalDeltaNormals.ToArray(), blendShape.finalDeltaTangents.ToArray() ); blendShape.blendShapeNameOnCombinedMesh = blendShapeName; if (alreadyAddedBlendShapesNames.ContainsKey(blendShape.blendShapeFrameName)) { alreadyAddedBlendShapesNames[blendShape.blendShapeFrameName] += 1; } else { alreadyAddedBlendShapesNames.Add(blendShape.blendShapeFrameName, 0); } } foreach (BlendShapeData blendShape in blendData) { meshRenderer.SetBlendShapeWeight( meshToRestoreTo.GetBlendShapeIndex(blendShape.blendShapeFrameName), blendShape.blendShapeCurrentValue ); } } } }