/// <summary> /// <para> /// Loads <see cref="Morph"/>s from a given key. This is done when the rootObject is initially set or when /// this CoreMorphs object is reset. /// </para> /// <para> /// Loading Morphs for CoreMorphs is handled by the <see cref="MCSCharacterManager"/> when it sets the /// <see cref="rootObject"/> for it's CoreMorphs object. Under normal circumstances a 3rd party developer /// would not need to use this method. /// </para> /// </summary> /// <param name="key">Should be the mesh.name, eg: Genesis2Female.Shape_LOD0</param> public void RefreshMorphsFromMeshKey(string key) { MorphManifest manifest = streamingMorphs.GetManifest(key, Application.isPlaying); //purge the old ones morphs.Clear(); morphLookup.Clear(); //dump the old groups morphStateGroups.Clear(); BuildDefaultStateMorphGroups(); foreach (string name in manifest.names) { Morph m = new Morph(name, 0f, false, false); #if !NIKEI_ENABLED if (m.name.ToLower().Contains("nikei")) { //Skip nikei continue; } #endif morphs.Add(m); morphLookup.Add(m.localName, m); //add this morph to the All group morphStateGroups["All"].Add(m); } //by default always sort the root morphs group alphabetically //SortMorphs(morphs); }
/// <summary> /// Rips out blendshapes from a skinned mesh renderer and figures out where to store it along with creating a manifest file /// </summary> /// <param name="smr"></param> /// <returns>A string path to the manifest file</returns> public static string ExtractBlendshapesFromMesh(SkinnedMeshRenderer smr, string dirPath, int totalProcessed = 0, int totalCount = 0, bool useProgressBar = true, bool generateManifest = true, NameScrubCallback smrScrub = null, NameScrubCallback morphScrub = null, List <string> nameWhitelist = null) { //Extracts all blendshapes out of the mesh, does not remove any of them from the mesh int blendshapeCount = smr.sharedMesh.blendShapeCount; string manifestPath = dirPath + "/manifest.json"; MorphManifest manifest = new MorphManifest(); manifest.name = smr.name; manifest.count = blendshapeCount; manifest.names = new string[blendshapeCount]; if (smrScrub != null) { manifest.name = smrScrub(manifest.name); } if (!Directory.Exists(dirPath)) { DirectoryInfo di = Directory.CreateDirectory(dirPath); } for (int i = 0; i < blendshapeCount; i++) { BlendshapeData bd = new BlendshapeData(); bd.name = smr.sharedMesh.GetBlendShapeName(i); if (morphScrub != null) { bd.name = morphScrub(bd.name); } bd.shapeIndex = i; int vertexCount = smr.sharedMesh.vertexCount; bd.deltaVertices = new Vector3[vertexCount]; bd.deltaNormals = new Vector3[vertexCount]; bd.deltaTangents = new Vector3[vertexCount]; //loads the blendshape data from the blendshape into our blendshapestate struct smr.sharedMesh.GetBlendShapeFrameVertices(bd.shapeIndex, bd.frameIndex, bd.deltaVertices, bd.deltaNormals, bd.deltaTangents); //convert a blendshape name from something like Genesis2Male__FBMHeavy to FBMHeavy int bdIndex = bd.name.LastIndexOf("__"); if (bdIndex > -1) { bd.name = bd.name.Substring(bdIndex + 2); } if (nameWhitelist != null && nameWhitelist.Count > 0) { if (!nameWhitelist.Contains(bd.name)) { continue; } else { UnityEngine.Debug.Log("Matched: " + bd.name); } } float percent = 0f; if (totalCount > 0) { percent = (float)totalProcessed / (float)totalCount; } if (useProgressBar) { //TODO: we need to move this back into editor land, as this function is ONLY used if you're using the production tool suite //EditorUtility.DisplayProgressBar("Extracting Blends", "Blend: " + bd.name, percent); } string relativePath = bd.name + "." + extension; string filePath = dirPath + "/" + relativePath; MCS_Utilities.Morph.MorphData morphData = new MCS_Utilities.Morph.MorphData(); morphData.name = bd.name; morphData.blendshapeData = bd; WriteMorphDataToFile(morphData, filePath, true, false); manifest.names[i] = bd.name; totalProcessed += 1; } if (generateManifest) { Stream fs = File.Create(manifestPath); string json = JsonUtility.ToJson(manifest); byte[] bytes = System.Text.Encoding.UTF8.GetBytes(json); fs.Write(bytes, 0, bytes.Length); fs.Close(); } return(manifestPath); }
/// <summary> /// Builds a manifest .json file ro the resources folder by scanning folders/files /// </summary> /// <returns>The manifest by crawling.</returns> /// <param name="baseName">This is a collection name, like MCS Female, Jersey Girl, etc</param> public static MorphManifest GenerateManifestByCrawling(string runtimePath, string baseName) { string baseDir = runtimePath;// + "/" + baseName; string manifestPath = runtimePath + "/" + "manifest.json"; string[] fileNames; List <string> names = new List <string>(); MorphManifest manifest = new MorphManifest(); if (Application.isEditor) { //if we're in editor land, we can do this efficiently by getting a list of files fileNames = Directory.GetFiles(baseDir, "*.morph*", SearchOption.AllDirectories); } else { //if we're not in editor land, we have to ask for the system to load the resources for us, and look at the names, this means they have to be loaded into memory UnityEngine.Object[] objs = Resources.LoadAll(baseDir); fileNames = new string[objs.Length]; int j = 0; foreach (GameObject obj in objs) { fileNames[j++] = obj.name; } //we don't need these anymore for (j = 0; j < objs.Length; j++) { UnityEngine.Object.Destroy(objs[j]); } } Regex pattern = new Regex(@"(\.morph\.gz|\.morph)$"); for (int i = 0; i < fileNames.Length; i++) { //is this a file we care about? Match m = pattern.Match(fileNames[i]); if (!m.Success) { continue; } //if we have a string like foo/bar/car.morph // we want "car" names.Add(Paths.GetFileNameFromFullPath(fileNames[i], true)); } manifest.name = baseName; manifest.count = names.Count; manifest.names = names.ToArray(); Stream fs = File.Create(manifestPath); string json = JsonUtility.ToJson(manifest); byte[] bytes = System.Text.Encoding.UTF8.GetBytes(json); fs.Write(bytes, 0, bytes.Length); fs.Close(); return(manifest); }