Esempio n. 1
0
        public static void WriteMorphDataToFile(MCS_Utilities.Morph.MorphData morphData, string filePath, bool refresh = false, bool compress = false)
        {
            if (!File.Exists(filePath) || refresh)
            {
                //FYI, I first tried a JSON ascii format but the file size was way too big, thus we're using a binary struct format
                //The blendshape cereal structure takes care of very efficiently packing a blendshape (about 28x byte compression)

                byte[] bytes = MCS_Utilities.Morph.MorphData.ConvertMorphDataToBytes(morphData);

                if (compress)
                {
                    Stream ms = new MemoryStream(bytes);
                    ms.Position = 0;
                    Stream outStream = Compression.CompressStream(ms);
                    outStream.Position = 0;
                    bytes = Compression.StreamToBytes(outStream);
                    ms.Close();
                    outStream.Close();
                }

                System.IO.File.WriteAllBytes(filePath, bytes);
            }
        }
Esempio n. 2
0
        /// <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);
        }
Esempio n. 3
0
        protected static char jctKeySeperatorChar = '|';  //used for splitting the name late


        /// <summary>
        /// Efficiently packs a morph into a hardcoded binary packed format
        ///  Using a dataset of about 30k verts...
        ///  C# native deserializer - 75ms
        ///  ProtoBuf deserializer - 30ms
        ///  Custom deserializer - 0ms
        /// </summary>
        public static byte[] ConvertMorphDataToBytes(MorphData morphData, int version = 1)
        {
            //Build the header, it's format is the following, note floats are all 4 bytes (single precision)
            // quick note, all lengths are of size "int" to keep things simple, saving a few extra bytes isn't worth worrying about it

            byte[] buffer;

            /*
             *
             * HEADER
             *
             * Magic Bytes - Verifies this is the correct file type (int)
             * Version (float) - This is the DATA STRUCTURE version, if we update the structure, we need to update the version
             * Length of Name (int)
             * Length of Delta Verts (int)
             * Length of Delta Norms (int)
             * Length of Delta Tans (int)
             * Length of JCT Keys (int) (about 170+ entries of variable length names)
             * Length of JCTs (int)
             *
             * BODY
             *
             * Name (string)
             * Verts (uint32+3floats)
             * Norms (uint32+3floats)
             * Tans (uint32+3floats)
             * JCT Names (string split on "|")
             * JCTs (6floats)
             *
             */


            MorphDataHeaderV1 header = new MorphDataHeaderV1();

            header.version = version;

            byte[] morphNameAsBytes = System.Text.Encoding.UTF8.GetBytes(morphData.name);

            header.lenName = morphNameAsBytes.Length;

            Vector3Spatial[] deltaVertices = Vector3Spatial.ConvertVector3ArrayToSpatialArray(morphData.blendshapeData.deltaVertices);
            Vector3Spatial[] deltaNormals  = Vector3Spatial.ConvertVector3ArrayToSpatialArray(morphData.blendshapeData.deltaNormals);
            Vector3Spatial[] deltaTangents = Vector3Spatial.ConvertVector3ArrayToSpatialArray(morphData.blendshapeData.deltaTangents);

            header.lenDeltaVertices = (morphData.blendshapeData.deltaVertices != null ? morphData.blendshapeData.deltaVertices.Length : 0);
            header.lenDeltaNormals  = (morphData.blendshapeData.deltaNormals != null ? morphData.blendshapeData.deltaNormals.Length : 0);
            header.lenDeltaTangents = (morphData.blendshapeData.deltaTangents != null ? morphData.blendshapeData.deltaTangents.Length : 0);

            header.lenDeltaVerticesPacked = (deltaVertices != null ? deltaVertices.Length : 0);
            header.lenDeltaNormalsPacked  = (deltaNormals != null ? deltaNormals.Length : 0);
            header.lenDeltaTangentsPacked = (deltaTangents != null ? deltaTangents.Length : 0);

            header.lenJCTKeys = 0;
            header.lenJCTs    = 0;

            StringBuilder sbJCTKeys = new StringBuilder();

            byte[] jctKeys = null;

            //only do this if we have jct data in here
            if (morphData.jctData.boneNames != null && morphData.jctData.boneNames.Length > 0)
            {
                header.lenJCTs = (ushort)morphData.jctData.localPositions.Length;
                for (int i = 0; i < morphData.jctData.boneNames.Length; i++)
                {
                    sbJCTKeys.Append(morphData.jctData.boneNames[i]);
                    if (i + 1 < morphData.jctData.boneNames.Length)
                    {
                        sbJCTKeys.Append(jctKeySeperator);
                    }
                }
                jctKeys           = System.Text.Encoding.UTF8.GetBytes(sbJCTKeys.ToString());
                header.lenJCTKeys = jctKeys.Length;
                //6 floats per item, key is the same as the name order
                header.lenJCTs = ((6 * sizeOfFloat) * morphData.jctData.localPositions.Length);
            }

            //now that we know the header, how big should our buffer be?
            int bufferSize = 0;

            bufferSize += sizeOfInt; //magic header
            bufferSize += sizeOfInt; //version
            bufferSize += sizeOfInt + header.lenName;
            bufferSize += sizeOfInt + (header.lenDeltaVerticesPacked * sizeOfInt) + (header.lenDeltaVerticesPacked * (3 * sizeOfFloat));
            bufferSize += sizeOfInt + (header.lenDeltaNormalsPacked * sizeOfInt) + (header.lenDeltaNormalsPacked * (3 * sizeOfFloat));
            bufferSize += sizeOfInt + (header.lenDeltaTangentsPacked * sizeOfInt) + (header.lenDeltaTangentsPacked * (3 * sizeOfFloat));
            bufferSize += (sizeOfInt * 3); //packed lengths
            bufferSize += sizeOfInt + header.lenJCTKeys;
            bufferSize += sizeOfInt + header.lenJCTs;
            //bufferSize += sizeOfInt + (6 * sizeOfFloat);


            buffer = new byte[bufferSize];

            byte[] itemBuffer;
            int    pos = 0;

            //write the header first

            //magic
            itemBuffer = System.BitConverter.GetBytes(magicHeader);
            System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
            pos += itemBuffer.Length;

            //version
            itemBuffer = System.BitConverter.GetBytes(header.version);
            System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
            pos += itemBuffer.Length;

            //name
            itemBuffer = System.BitConverter.GetBytes(header.lenName);
            System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
            pos += itemBuffer.Length;

            //verts (unpacked)
            itemBuffer = System.BitConverter.GetBytes(header.lenDeltaVertices);
            System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
            pos += itemBuffer.Length;

            //normals
            itemBuffer = System.BitConverter.GetBytes(header.lenDeltaNormals);
            System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
            pos += itemBuffer.Length;

            //tangents
            itemBuffer = System.BitConverter.GetBytes(header.lenDeltaTangents);
            System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
            pos += itemBuffer.Length;

            //packed
            itemBuffer = System.BitConverter.GetBytes(header.lenDeltaVerticesPacked);
            System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
            pos       += itemBuffer.Length;
            itemBuffer = System.BitConverter.GetBytes(header.lenDeltaNormalsPacked);
            System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
            pos       += itemBuffer.Length;
            itemBuffer = System.BitConverter.GetBytes(header.lenDeltaTangentsPacked);
            System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
            pos += itemBuffer.Length;

            //JCT keys
            itemBuffer = System.BitConverter.GetBytes(header.lenJCTKeys);
            System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
            pos += itemBuffer.Length;

            //JCTs
            itemBuffer = System.BitConverter.GetBytes(header.lenJCTs);
            System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
            pos += itemBuffer.Length;

            //header is done, now let's write the body

            //morph name
            System.Buffer.BlockCopy(morphNameAsBytes, 0, buffer, pos, morphNameAsBytes.Length);
            pos += morphNameAsBytes.Length;

            //deltas

            if (deltaVertices != null)
            {
                for (int i = 0; i < deltaVertices.Length; i++)
                {
                    itemBuffer = System.BitConverter.GetBytes(deltaVertices[i].index);
                    System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
                    pos       += itemBuffer.Length;
                    itemBuffer = System.BitConverter.GetBytes(deltaVertices[i].x);
                    System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
                    pos       += itemBuffer.Length;
                    itemBuffer = System.BitConverter.GetBytes(deltaVertices[i].y);
                    System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
                    pos       += itemBuffer.Length;
                    itemBuffer = System.BitConverter.GetBytes(deltaVertices[i].z);
                    System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
                    pos += itemBuffer.Length;
                }
            }
            if (deltaNormals != null)
            {
                for (int i = 0; i < deltaNormals.Length; i++)
                {
                    itemBuffer = System.BitConverter.GetBytes(deltaVertices[i].index);
                    System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
                    pos       += itemBuffer.Length;
                    itemBuffer = System.BitConverter.GetBytes(deltaNormals[i].x);
                    System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
                    pos       += itemBuffer.Length;
                    itemBuffer = System.BitConverter.GetBytes(deltaNormals[i].y);
                    System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
                    pos       += itemBuffer.Length;
                    itemBuffer = System.BitConverter.GetBytes(deltaNormals[i].z);
                    System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
                    pos += itemBuffer.Length;
                }
            }
            if (deltaTangents != null)
            {
                for (int i = 0; i < deltaTangents.Length; i++)
                {
                    itemBuffer = System.BitConverter.GetBytes(deltaVertices[i].index);
                    System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
                    pos       += itemBuffer.Length;
                    itemBuffer = System.BitConverter.GetBytes(deltaTangents[i].x);
                    System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
                    pos       += itemBuffer.Length;
                    itemBuffer = System.BitConverter.GetBytes(deltaTangents[i].y);
                    System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
                    pos       += itemBuffer.Length;
                    itemBuffer = System.BitConverter.GetBytes(deltaTangents[i].z);
                    System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
                    pos += itemBuffer.Length;
                }
            }

            //JCT
            if (jctKeys != null)
            {
                //JCT keys
                System.Buffer.BlockCopy(jctKeys, 0, buffer, pos, jctKeys.Length);
                pos += jctKeys.Length;


                //JCTs
                for (int i = 0; i < morphData.jctData.localPositions.Length; i++)
                {
                    itemBuffer = System.BitConverter.GetBytes(morphData.jctData.localPositions[i].x);
                    System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
                    pos       += itemBuffer.Length;
                    itemBuffer = System.BitConverter.GetBytes(morphData.jctData.localPositions[i].y);
                    System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
                    pos       += itemBuffer.Length;
                    itemBuffer = System.BitConverter.GetBytes(morphData.jctData.localPositions[i].z);
                    System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
                    pos += itemBuffer.Length;


                    itemBuffer = System.BitConverter.GetBytes(morphData.jctData.worldPositions[i].x);
                    System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
                    pos       += itemBuffer.Length;
                    itemBuffer = System.BitConverter.GetBytes(morphData.jctData.worldPositions[i].y);
                    System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
                    pos       += itemBuffer.Length;
                    itemBuffer = System.BitConverter.GetBytes(morphData.jctData.worldPositions[i].z);
                    System.Buffer.BlockCopy(itemBuffer, 0, buffer, pos, itemBuffer.Length);
                    pos += itemBuffer.Length;
                }
            }


            return(buffer);
        }
Esempio n. 4
0
        public static MorphData ConvertBytesToMorphData(byte[] buffer)
        {
            MorphData md = new MorphData();

            int pos = 0;

            //magic bytes
            int magicHeaderIn = System.BitConverter.ToInt32(buffer, pos);

            pos += sizeOfInt;

            if (magicHeader != magicHeaderIn)
            {
                throw new Exception("Magic morph header not found, invalid morph file");
            }

            int version = System.BitConverter.ToInt32(buffer, pos);

            pos += sizeOfInt;

            switch (version)
            {
            case 1:     //Version 1
                MorphDataHeaderV1 header = new MorphDataHeaderV1();
                header.version                = version;
                header.lenName                = System.BitConverter.ToInt32(buffer, pos);
                pos                          += sizeOfInt;
                header.lenDeltaVertices       = System.BitConverter.ToInt32(buffer, pos);
                pos                          += sizeOfInt;
                header.lenDeltaNormals        = System.BitConverter.ToInt32(buffer, pos);
                pos                          += sizeOfInt;
                header.lenDeltaTangents       = System.BitConverter.ToInt32(buffer, pos);
                pos                          += sizeOfInt;
                header.lenDeltaVerticesPacked = System.BitConverter.ToInt32(buffer, pos);
                pos                          += sizeOfInt;
                header.lenDeltaNormalsPacked  = System.BitConverter.ToInt32(buffer, pos);
                pos                          += sizeOfInt;
                header.lenDeltaTangentsPacked = System.BitConverter.ToInt32(buffer, pos);
                pos                          += sizeOfInt;
                header.lenJCTKeys             = System.BitConverter.ToInt32(buffer, pos);
                pos                          += sizeOfInt;
                header.lenJCTs                = System.BitConverter.ToInt32(buffer, pos);
                pos                          += sizeOfInt;

                //name
                byte[] nameBytes = new byte[header.lenName];
                System.Buffer.BlockCopy(buffer, pos, nameBytes, 0, header.lenName);
                md.name = System.Text.Encoding.UTF8.GetString(nameBytes);
                md.blendshapeData.name = md.name;
                pos += header.lenName;

                Vector3Spatial[] deltaVerticesCereal = new Vector3Spatial[header.lenDeltaVerticesPacked];
                Vector3Spatial[] deltaNormalsCereal  = new Vector3Spatial[header.lenDeltaNormalsPacked];
                Vector3Spatial[] deltaTangentsCereal = new Vector3Spatial[header.lenDeltaTangentsPacked];

                for (int i = 0; i < header.lenDeltaVerticesPacked; i++)
                {
                    deltaVerticesCereal[i].index = System.BitConverter.ToInt32(buffer, pos);
                    pos += sizeOfInt;
                    deltaVerticesCereal[i].x = System.BitConverter.ToSingle(buffer, pos);
                    pos += sizeOfFloat;
                    deltaVerticesCereal[i].y = System.BitConverter.ToSingle(buffer, pos);
                    pos += sizeOfFloat;
                    deltaVerticesCereal[i].z = System.BitConverter.ToSingle(buffer, pos);
                    pos += sizeOfFloat;
                }

                for (int i = 0; i < header.lenDeltaNormalsPacked; i++)
                {
                    deltaNormalsCereal[i].index = System.BitConverter.ToInt32(buffer, pos);
                    pos += sizeOfInt;
                    deltaNormalsCereal[i].x = System.BitConverter.ToSingle(buffer, pos);
                    pos += sizeOfFloat;
                    deltaNormalsCereal[i].y = System.BitConverter.ToSingle(buffer, pos);
                    pos += sizeOfFloat;
                    deltaNormalsCereal[i].z = System.BitConverter.ToSingle(buffer, pos);
                    pos += sizeOfFloat;
                }

                for (int i = 0; i < header.lenDeltaTangentsPacked; i++)
                {
                    deltaTangentsCereal[i].index = System.BitConverter.ToInt32(buffer, pos);
                    pos += sizeOfInt;
                    deltaTangentsCereal[i].x = System.BitConverter.ToSingle(buffer, pos);
                    pos += sizeOfFloat;
                    deltaTangentsCereal[i].y = System.BitConverter.ToSingle(buffer, pos);
                    pos += sizeOfFloat;
                    deltaTangentsCereal[i].z = System.BitConverter.ToSingle(buffer, pos);
                    pos += sizeOfFloat;
                }

                //blendshape states allow nulls for normals and tangents, but not vertices, by doing this check we reduce the liklihood we need to expand the heap
                md.blendshapeData.deltaVertices = new Vector3[header.lenDeltaVertices];
                Vector3Spatial.ConvertVector3CerealToArrayNonAlloc(deltaVerticesCereal, md.blendshapeData.deltaVertices);
                if (header.lenDeltaNormalsPacked > 0)
                {
                    md.blendshapeData.deltaNormals = new Vector3[header.lenDeltaNormals];
                    Vector3Spatial.ConvertVector3CerealToArrayNonAlloc(deltaNormalsCereal, md.blendshapeData.deltaNormals);
                }
                if (header.lenDeltaTangentsPacked > 0)
                {
                    md.blendshapeData.deltaTangents = new Vector3[header.lenDeltaTangents];
                    Vector3Spatial.ConvertVector3CerealToArrayNonAlloc(deltaTangentsCereal, md.blendshapeData.deltaTangents);
                }


                //finally let's read jcts if we have them
                if (header.lenJCTKeys > 0)
                {
                    byte[] JCTKeysBytes = new byte[header.lenJCTKeys];
                    System.Buffer.BlockCopy(buffer, pos, JCTKeysBytes, 0, header.lenJCTKeys);
                    string JCTKeysString = System.Text.Encoding.UTF8.GetString(JCTKeysBytes);
                    pos += header.lenJCTKeys;

                    md.jctData.boneNames = JCTKeysString.Split(jctKeySeperatorChar);

                    int totalJCTCount = header.lenJCTKeys / (6 * sizeOfFloat);

                    md.jctData.localPositions = new Vector3[totalJCTCount];
                    md.jctData.worldPositions = new Vector3[totalJCTCount];

                    if (header.lenJCTs > 0)
                    {
                        for (int i = 0; i < totalJCTCount; i++)
                        {
                            md.jctData.localPositions[i].x = System.BitConverter.ToSingle(buffer, pos);
                            pos += sizeOfFloat;
                            md.jctData.localPositions[i].y = System.BitConverter.ToSingle(buffer, pos);
                            pos += sizeOfFloat;
                            md.jctData.localPositions[i].z = System.BitConverter.ToSingle(buffer, pos);
                            pos += sizeOfFloat;
                        }
                        for (int i = 0; i < totalJCTCount; i++)
                        {
                            md.jctData.worldPositions[i].x = System.BitConverter.ToSingle(buffer, pos);
                            pos += sizeOfFloat;
                            md.jctData.worldPositions[i].y = System.BitConverter.ToSingle(buffer, pos);
                            pos += sizeOfFloat;
                            md.jctData.worldPositions[i].z = System.BitConverter.ToSingle(buffer, pos);
                            pos += sizeOfFloat;
                        }
                    }
                }

                break;

            default:
                UnityEngine.Debug.LogError("Version: " + version + " is not supported, please verify you have the most up-to-date version of MCS");
                throw new Exception("Unknown morph file version");
                break;
            }

            return(md);
        }