Bunny83

MeshSerializer

Feb 19th, 2018
2,818
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #region License and information
  2. /* * * * *
  3.  * A quick mesh serializer that allows to serialize a Mesh as byte array. It should
  4.  * support any kind of mesh including skinned meshes, multiple submeshes, different
  5.  * mesh topologies as well as blendshapes. I tried my best to avoid unnecessary data
  6.  * by only serializing information that is present. It supports Vector4 UVs. The index
  7.  * data may be stored as bytes, ushorts or ints depending on the actual highest used
  8.  * vertex index within a submesh. It uses a tagging system for optional "chunks". The
  9.  * base information only includes the vertex position array and the submesh count.
  10.  * Everything else is handled through optional chunks.
  11.  *
  12.  *
  13.  * The MIT License (MIT)
  14.  *
  15.  * Copyright (c) 2018 Markus Göbel (Bunny83)
  16.  *
  17.  * Permission is hereby granted, free of charge, to any person obtaining a copy
  18.  * of this software and associated documentation files (the "Software"), to deal
  19.  * in the Software without restriction, including without limitation the rights
  20.  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  21.  * copies of the Software, and to permit persons to whom the Software is
  22.  * furnished to do so, subject to the following conditions:
  23.  *
  24.  * The above copyright notice and this permission notice shall be included in all
  25.  * copies or substantial portions of the Software.
  26.  *
  27.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  28.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  29.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  30.  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  31.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  32.  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  33.  * SOFTWARE.
  34.  *
  35.  * * * * */
  36. #endregion License and information
  37.  
  38. namespace B83.MeshTools
  39. {
  40.     using System.IO;
  41.     using System.Collections.Generic;
  42.     using UnityEngine;
  43.     using System.Linq;
  44.  
  45.     [System.Serializable]
  46.     public class MeshData
  47.     {
  48.         [SerializeField, HideInInspector]
  49.         private byte[] m_Data;
  50.         private Mesh m_Mesh;
  51.         public byte[] Data { get{ return m_Data; } }
  52.         public void SetMesh(Mesh aMesh)
  53.         {
  54.             m_Mesh = aMesh;
  55.             if (aMesh == null)
  56.                 m_Data = null;
  57.             else
  58.                 m_Data = MeshSerializer.SerializeMesh(m_Mesh);
  59.         }
  60.         public Mesh GetMesh()
  61.         {
  62.             if (m_Mesh == null && m_Data != null)
  63.                 m_Mesh = MeshSerializer.DeserializeMesh(m_Data);
  64.             return m_Mesh;
  65.         }
  66.     }
  67.  
  68.  
  69.     public static class MeshSerializer
  70.     {
  71.         /*
  72.          * Structure:
  73.          * - Magic string "Mesh" (4 bytes)
  74.          * - vertex count [int] (4 bytes)
  75.          * - submesh count [int] (4 bytes)
  76.          * - vertices [array of Vector3]
  77.          *
  78.          * - additional chunks:
  79.          *   [vertex attributes]
  80.          *   - Name (name of the Mesh object)
  81.          *   - Normals [array of Vector3]
  82.          *   - Tangents [array of Vector4]
  83.          *   - Colors [array of Color32]
  84.          *   - UV0-4 [
  85.          *       - component count[byte](2/3/4)
  86.          *       - array of Vector2/3/4
  87.          *     ]
  88.          *   - BoneWeights [array of 4x int+float pair]
  89.          *  
  90.          *   [other data]
  91.          *   - Submesh [
  92.          *       - topology[byte]
  93.          *       - count[int]
  94.          *       - component size[byte](1/2/4)
  95.          *       - array of byte/ushort/int
  96.          *     ]
  97.          *   - Bindposes [
  98.          *       - count[int]
  99.          *       - array of Matrix4x4
  100.          *     ]
  101.          *   - BlendShape [
  102.          *       - Name [string]
  103.          *       - frameCount [int]
  104.          *       - frames [ array of:
  105.          *           - frameWeight [float]
  106.          *           - array of [
  107.          *               - position delta [Vector3]
  108.          *               - normal delta [Vector3]
  109.          *               - tangent delta [Vector3]
  110.          *             ]
  111.          *         ]
  112.          *     ]
  113.          */
  114.         private enum EChunkID : byte
  115.         {
  116.             End,
  117.             Name,
  118.             Normals,
  119.             Tangents,
  120.             Colors,
  121.             BoneWeights,
  122.             UV0, UV1, UV2, UV3,
  123.             Submesh,
  124.             Bindposes,
  125.             BlendShape,
  126.         }
  127.         const uint m_Magic = 0x6873654D; // "Mesh"
  128.  
  129.         public static byte[] SerializeMesh(Mesh aMesh)
  130.         {
  131.             using (var stream = new MemoryStream())
  132.             {
  133.                 SerializeMesh(stream, aMesh);
  134.                 return stream.ToArray();
  135.             }
  136.         }
  137.         public static void SerializeMesh(MemoryStream aStream, Mesh aMesh)
  138.         {
  139.             using (var writer = new BinaryWriter(aStream))
  140.                 SerializeMesh(writer, aMesh);
  141.         }
  142.         public static void SerializeMesh(BinaryWriter aWriter, Mesh aMesh)
  143.         {
  144.             aWriter.Write(m_Magic);
  145.             var vertices = aMesh.vertices;
  146.             int count = vertices.Length;
  147.             int subMeshCount = aMesh.subMeshCount;
  148.             aWriter.Write(count);
  149.             aWriter.Write(subMeshCount);
  150.             foreach (var v in vertices)
  151.                 aWriter.WriteVector3(v);
  152.  
  153.             // start of tagged chunks
  154.             if (!string.IsNullOrEmpty(aMesh.name))
  155.             {
  156.                 aWriter.Write((byte)EChunkID.Name);
  157.                 aWriter.Write(aMesh.name);
  158.             }
  159.             var normals = aMesh.normals;
  160.             if (normals != null && normals.Length == count)
  161.             {
  162.                 aWriter.Write((byte)EChunkID.Normals);
  163.                 foreach (var v in normals)
  164.                     aWriter.WriteVector3(v);
  165.                 normals = null;
  166.             }
  167.             var tangents = aMesh.tangents;
  168.             if (tangents != null && tangents.Length == count)
  169.             {
  170.                 aWriter.Write((byte)EChunkID.Tangents);
  171.                 foreach (var v in tangents)
  172.                     aWriter.WriteVector4(v);
  173.                 tangents = null;
  174.             }
  175.             var colors = aMesh.colors32;
  176.             if (colors != null && colors.Length == count)
  177.             {
  178.                 aWriter.Write((byte)EChunkID.Colors);
  179.                 foreach (var c in colors)
  180.                     aWriter.WriteColor32(c);
  181.                 colors = null;
  182.             }
  183.             var boneWeights = aMesh.boneWeights;
  184.             if (boneWeights != null && boneWeights.Length == count)
  185.             {
  186.                 aWriter.Write((byte)EChunkID.BoneWeights);
  187.                 foreach (var w in boneWeights)
  188.                     aWriter.WriteBoneWeight(w);
  189.                 boneWeights = null;
  190.             }
  191.             List<Vector4> uvs = new List<Vector4>();
  192.             for (int i = 0; i < 4; i++)
  193.             {
  194.                 uvs.Clear();
  195.                 aMesh.GetUVs(i, uvs);
  196.                 if (uvs.Count == count)
  197.                 {
  198.                     aWriter.Write((byte)((byte)EChunkID.UV0 + i));
  199.                     byte channelCount = 2;
  200.                     foreach (var uv in uvs)
  201.                     {
  202.                         if (uv.z != 0f)
  203.                             channelCount = 3;
  204.                         if (uv.w != 0f)
  205.                         {
  206.                             channelCount = 4;
  207.                             break;
  208.                         }
  209.                     }
  210.                     aWriter.Write(channelCount);
  211.                     if (channelCount == 2)
  212.                         foreach (var uv in uvs)
  213.                             aWriter.WriteVector2(uv);
  214.                     else if (channelCount == 3)
  215.                         foreach (var uv in uvs)
  216.                             aWriter.WriteVector3(uv);
  217.                     else
  218.                         foreach (var uv in uvs)
  219.                             aWriter.WriteVector4(uv);
  220.                 }
  221.             }
  222.             List<int> indices = new List<int>(count * 3);
  223.             for (int i = 0; i < subMeshCount; i++)
  224.             {
  225.                 indices.Clear();
  226.                 aMesh.GetIndices(indices, i);
  227.                 if (indices.Count > 0)
  228.                 {
  229.                     aWriter.Write((byte)EChunkID.Submesh);
  230.                     aWriter.Write((byte)aMesh.GetTopology(i));
  231.                     aWriter.Write(indices.Count);
  232.                     var max = indices.Max();
  233.                     if (max < 256)
  234.                     {
  235.                         aWriter.Write((byte)1);
  236.                         foreach (var index in indices)
  237.                             aWriter.Write((byte)index);
  238.                     }
  239.                     else if (max < 65536)
  240.                     {
  241.                         aWriter.Write((byte)2);
  242.                         foreach (var index in indices)
  243.                             aWriter.Write((ushort)index);
  244.                     }
  245.                     else
  246.                     {
  247.                         aWriter.Write((byte)4);
  248.                         foreach (var index in indices)
  249.                             aWriter.Write(index);
  250.                     }
  251.                 }
  252.             }
  253.             var bindposes = aMesh.bindposes;
  254.             if (bindposes != null && bindposes.Length > 0)
  255.             {
  256.                 aWriter.Write((byte)EChunkID.Bindposes);
  257.                 aWriter.Write(bindposes.Length);
  258.                 foreach (var b in bindposes)
  259.                     aWriter.WriteMatrix4x4(b);
  260.                 bindposes = null;
  261.             }
  262.             int blendShapeCount = aMesh.blendShapeCount;
  263.             if (blendShapeCount > 0)
  264.             {
  265.                 var blendVerts = new Vector3[count];
  266.                 var blendNormals = new Vector3[count];
  267.                 var blendTangents = new Vector3[count];
  268.                 for (int i = 0; i < blendShapeCount; i++)
  269.                 {
  270.                     aWriter.Write((byte)EChunkID.BlendShape);
  271.                     aWriter.Write(aMesh.GetBlendShapeName(i));
  272.                     var frameCount = aMesh.GetBlendShapeFrameCount(i);
  273.                     aWriter.Write(frameCount);
  274.                     for (int n = 0; n < frameCount; n++)
  275.                     {
  276.                         aMesh.GetBlendShapeFrameVertices(i, n, blendVerts, blendNormals, blendTangents);
  277.                         aWriter.Write(aMesh.GetBlendShapeFrameWeight(i, n));
  278.                         for (int k = 0; k < count; k++)
  279.                         {
  280.                             aWriter.WriteVector3(blendVerts[k]);
  281.                             aWriter.WriteVector3(blendNormals[k]);
  282.                             aWriter.WriteVector3(blendTangents[k]);
  283.                         }
  284.                     }
  285.                 }
  286.             }
  287.             aWriter.Write((byte)EChunkID.End);
  288.         }
  289.  
  290.  
  291.         public static Mesh DeserializeMesh(byte[] aData, Mesh aTarget = null)
  292.         {
  293.             using (var stream = new MemoryStream(aData))
  294.                 return DeserializeMesh(stream, aTarget);
  295.         }
  296.         public static Mesh DeserializeMesh(MemoryStream aStream, Mesh aTarget = null)
  297.         {
  298.             using (var reader = new BinaryReader(aStream))
  299.                 return DeserializeMesh(reader, aTarget);
  300.         }
  301.         public static Mesh DeserializeMesh(BinaryReader aReader, Mesh aTarget = null)
  302.         {
  303.             if (aReader.ReadUInt32() != m_Magic)
  304.                 return null;
  305.             if (aTarget == null)
  306.                 aTarget = new Mesh();
  307.             aTarget.Clear();
  308.             aTarget.ClearBlendShapes();
  309.             int count = aReader.ReadInt32();
  310.             if (count > 65534)
  311.                 aTarget.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
  312.             int subMeshCount = aReader.ReadInt32();
  313.             Vector3[] vector3Array = new Vector3[count];
  314.             Vector3[] vector3Array2 = null;
  315.             Vector3[] vector3Array3 = null;
  316.             List<Vector4> vector4List = null;
  317.             for (int i = 0; i < count; i++)
  318.                 vector3Array[i] = aReader.ReadVector3();
  319.             aTarget.vertices = vector3Array;
  320.             aTarget.subMeshCount = subMeshCount;
  321.             int subMeshIndex = 0;
  322.             byte componentCount = 0;
  323.  
  324.             // reading chunks
  325.             var stream = aReader.BaseStream;
  326.             while ((stream.CanSeek && stream.Position < stream.Length) || stream.CanRead)
  327.             {
  328.                 var chunkID = (EChunkID)aReader.ReadByte();
  329.                 if (chunkID == EChunkID.End)
  330.                     break;
  331.                 switch (chunkID)
  332.                 {
  333.                     case EChunkID.Name:
  334.                         aTarget.name = aReader.ReadString();
  335.                         break;
  336.                     case EChunkID.Normals:
  337.                         for (int i = 0; i < count; i++)
  338.                             vector3Array[i] = aReader.ReadVector3();
  339.                         aTarget.normals = vector3Array;
  340.                         break;
  341.                     case EChunkID.Tangents:
  342.                         if (vector4List == null)
  343.                             vector4List = new List<Vector4>(count);
  344.                         vector4List.Clear();
  345.                         for (int i = 0; i < count; i++)
  346.                             vector4List.Add(aReader.ReadVector4());
  347.                         aTarget.SetTangents(vector4List);
  348.                         break;
  349.                     case EChunkID.Colors:
  350.                         var colors = new Color32[count];
  351.                         for (int i = 0; i < count; i++)
  352.                             colors[i] = aReader.ReadColor32();
  353.                         aTarget.colors32 = colors;
  354.                         break;
  355.                     case EChunkID.BoneWeights:
  356.                         var boneWeights = new BoneWeight[count];
  357.                         for (int i = 0; i < count; i++)
  358.                             boneWeights[i] = aReader.ReadBoneWeight();
  359.                         aTarget.boneWeights = boneWeights;
  360.                         break;
  361.                     case EChunkID.UV0:
  362.                     case EChunkID.UV1:
  363.                     case EChunkID.UV2:
  364.                     case EChunkID.UV3:
  365.                         int uvChannel = chunkID - EChunkID.UV0;
  366.                         componentCount = aReader.ReadByte();
  367.                         if (vector4List == null)
  368.                             vector4List = new List<Vector4>(count);
  369.                         vector4List.Clear();
  370.  
  371.                         if (componentCount == 2)
  372.                         {
  373.                             for (int i = 0; i < count; i++)
  374.                                 vector4List.Add(aReader.ReadVector2());
  375.                         }
  376.                         else if (componentCount == 3)
  377.                         {
  378.                             for (int i = 0; i < count; i++)
  379.                                 vector4List.Add(aReader.ReadVector3());
  380.                         }
  381.                         else if (componentCount == 4)
  382.                         {
  383.                             for (int i = 0; i < count; i++)
  384.                                 vector4List.Add(aReader.ReadVector4());
  385.                         }
  386.                         aTarget.SetUVs(uvChannel, vector4List);
  387.                         break;
  388.                     case EChunkID.Submesh:
  389.                         var topology = (MeshTopology)aReader.ReadByte();
  390.                         int indexCount = aReader.ReadInt32();
  391.                         var indices = new int[indexCount];
  392.                         componentCount = aReader.ReadByte();
  393.                         if (componentCount == 1)
  394.                         {
  395.                             for (int i = 0; i < indexCount; i++)
  396.                                 indices[i] = aReader.ReadByte();
  397.                         }
  398.                         else if (componentCount == 2)
  399.                         {
  400.                             for (int i = 0; i < indexCount; i++)
  401.                                 indices[i] = aReader.ReadUInt16();
  402.                         }
  403.                         else if (componentCount == 4)
  404.                         {
  405.                             for (int i = 0; i < indexCount; i++)
  406.                                 indices[i] = aReader.ReadInt32();
  407.                         }
  408.                         aTarget.SetIndices(indices, topology, subMeshIndex++, false);
  409.                         break;
  410.                     case EChunkID.Bindposes:
  411.                         int bindposesCount = aReader.ReadInt32();
  412.                         var bindposes = new Matrix4x4[bindposesCount];
  413.                         for (int i = 0; i < bindposesCount; i++)
  414.                             bindposes[i] = aReader.ReadMatrix4x4();
  415.                         aTarget.bindposes = bindposes;
  416.                         break;
  417.                     case EChunkID.BlendShape:
  418.                         var blendShapeName = aReader.ReadString();
  419.                         int frameCount = aReader.ReadInt32();
  420.                         if (vector3Array2 == null)
  421.                             vector3Array2 = new Vector3[count];
  422.                         if (vector3Array3 == null)
  423.                             vector3Array3 = new Vector3[count];
  424.                         for (int i = 0; i < frameCount; i++)
  425.                         {
  426.                             float weight = aReader.ReadSingle();
  427.                             for (int n = 0; n < count; n++)
  428.                             {
  429.                                 vector3Array[n] = aReader.ReadVector3();
  430.                                 vector3Array2[n] = aReader.ReadVector3();
  431.                                 vector3Array3[n] = aReader.ReadVector3();
  432.                             }
  433.                             aTarget.AddBlendShapeFrame(blendShapeName, weight, vector3Array, vector3Array2, vector3Array3);
  434.                         }
  435.                         break;
  436.                 }
  437.             }
  438.             return aTarget;
  439.         }
  440.     }
  441.  
  442.  
  443.     public static class BinaryReaderWriterUnityExt
  444.     {
  445.         public static void WriteVector2(this BinaryWriter aWriter, Vector2 aVec)
  446.         {
  447.             aWriter.Write(aVec.x); aWriter.Write(aVec.y);
  448.         }
  449.         public static Vector2 ReadVector2(this BinaryReader aReader)
  450.         {
  451.             return new Vector2(aReader.ReadSingle(), aReader.ReadSingle());
  452.         }
  453.         public static void WriteVector3(this BinaryWriter aWriter, Vector3 aVec)
  454.         {
  455.             aWriter.Write(aVec.x); aWriter.Write(aVec.y); aWriter.Write(aVec.z);
  456.         }
  457.         public static Vector3 ReadVector3(this BinaryReader aReader)
  458.         {
  459.             return new Vector3(aReader.ReadSingle(), aReader.ReadSingle(), aReader.ReadSingle());
  460.         }
  461.         public static void WriteVector4(this BinaryWriter aWriter, Vector4 aVec)
  462.         {
  463.             aWriter.Write(aVec.x); aWriter.Write(aVec.y); aWriter.Write(aVec.z); aWriter.Write(aVec.w);
  464.         }
  465.         public static Vector4 ReadVector4(this BinaryReader aReader)
  466.         {
  467.             return new Vector4(aReader.ReadSingle(), aReader.ReadSingle(), aReader.ReadSingle(), aReader.ReadSingle());
  468.         }
  469.  
  470.         public static void WriteColor32(this BinaryWriter aWriter, Color32 aCol)
  471.         {
  472.             aWriter.Write(aCol.r); aWriter.Write(aCol.g); aWriter.Write(aCol.b); aWriter.Write(aCol.a);
  473.         }
  474.         public static Color32 ReadColor32(this BinaryReader aReader)
  475.         {
  476.             return new Color32(aReader.ReadByte(), aReader.ReadByte(), aReader.ReadByte(), aReader.ReadByte());
  477.         }
  478.  
  479.         public static void WriteMatrix4x4(this BinaryWriter aWriter, Matrix4x4 aMat)
  480.         {
  481.             aWriter.Write(aMat.m00); aWriter.Write(aMat.m01); aWriter.Write(aMat.m02); aWriter.Write(aMat.m03);
  482.             aWriter.Write(aMat.m10); aWriter.Write(aMat.m11); aWriter.Write(aMat.m12); aWriter.Write(aMat.m13);
  483.             aWriter.Write(aMat.m20); aWriter.Write(aMat.m21); aWriter.Write(aMat.m22); aWriter.Write(aMat.m23);
  484.             aWriter.Write(aMat.m30); aWriter.Write(aMat.m31); aWriter.Write(aMat.m32); aWriter.Write(aMat.m33);
  485.         }
  486.         public static Matrix4x4 ReadMatrix4x4(this BinaryReader aReader)
  487.         {
  488.             var m = new Matrix4x4();
  489.             m.m00 = aReader.ReadSingle(); m.m01 = aReader.ReadSingle(); m.m02 = aReader.ReadSingle(); m.m03 = aReader.ReadSingle();
  490.             m.m10 = aReader.ReadSingle(); m.m11 = aReader.ReadSingle(); m.m12 = aReader.ReadSingle(); m.m13 = aReader.ReadSingle();
  491.             m.m20 = aReader.ReadSingle(); m.m21 = aReader.ReadSingle(); m.m22 = aReader.ReadSingle(); m.m23 = aReader.ReadSingle();
  492.             m.m30 = aReader.ReadSingle(); m.m31 = aReader.ReadSingle(); m.m32 = aReader.ReadSingle(); m.m33 = aReader.ReadSingle();
  493.             return m;
  494.         }
  495.  
  496.         public static void WriteBoneWeight(this BinaryWriter aWriter, BoneWeight aWeight)
  497.         {
  498.             aWriter.Write(aWeight.boneIndex0); aWriter.Write(aWeight.weight0);
  499.             aWriter.Write(aWeight.boneIndex1); aWriter.Write(aWeight.weight1);
  500.             aWriter.Write(aWeight.boneIndex2); aWriter.Write(aWeight.weight2);
  501.             aWriter.Write(aWeight.boneIndex3); aWriter.Write(aWeight.weight3);
  502.         }
  503.         public static BoneWeight ReadBoneWeight(this BinaryReader aReader)
  504.         {
  505.             var w = new BoneWeight();
  506.             w.boneIndex0 = aReader.ReadInt32(); w.weight0 = aReader.ReadSingle();
  507.             w.boneIndex1 = aReader.ReadInt32(); w.weight1 = aReader.ReadSingle();
  508.             w.boneIndex2 = aReader.ReadInt32(); w.weight2 = aReader.ReadSingle();
  509.             w.boneIndex3 = aReader.ReadInt32(); w.weight3 = aReader.ReadSingle();
  510.             return w;
  511.         }
  512.     }
  513. }
RAW Paste Data

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×