Example #1
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);
        }
Example #2
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);
        }