SHARE
TWEET

MeshSerializer

Bunny83 Feb 19th, 2018 605 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.             int subMeshCount = aReader.ReadInt32();
  311.             Vector3[] vector3Array = new Vector3[count];
  312.             Vector3[] vector3Array2 = null;
  313.             Vector3[] vector3Array3 = null;
  314.             List<Vector4> vector4List = null;
  315.             for (int i = 0; i < count; i++)
  316.                 vector3Array[i] = aReader.ReadVector3();
  317.             aTarget.vertices = vector3Array;
  318.             aTarget.subMeshCount = subMeshCount;
  319.             int subMeshIndex = 0;
  320.             byte componentCount = 0;
  321.  
  322.             // reading chunks
  323.             var stream = aReader.BaseStream;
  324.             while ((stream.CanSeek && stream.Position < stream.Length) || stream.CanRead)
  325.             {
  326.                 var chunkID = (EChunkID)aReader.ReadByte();
  327.                 if (chunkID == EChunkID.End)
  328.                     break;
  329.                 switch (chunkID)
  330.                 {
  331.                     case EChunkID.Name:
  332.                         aTarget.name = aReader.ReadString();
  333.                         break;
  334.                     case EChunkID.Normals:
  335.                         for (int i = 0; i < count; i++)
  336.                             vector3Array[i] = aReader.ReadVector3();
  337.                         aTarget.normals = vector3Array;
  338.                         break;
  339.                     case EChunkID.Tangents:
  340.                         if (vector4List == null)
  341.                             vector4List = new List<Vector4>(count);
  342.                         vector4List.Clear();
  343.                         for (int i = 0; i < count; i++)
  344.                             vector4List.Add(aReader.ReadVector4());
  345.                         aTarget.SetTangents(vector4List);
  346.                         break;
  347.                     case EChunkID.Colors:
  348.                         var colors = new Color32[count];
  349.                         for (int i = 0; i < count; i++)
  350.                             colors[i] = aReader.ReadColor32();
  351.                         aTarget.colors32 = colors;
  352.                         break;
  353.                     case EChunkID.BoneWeights:
  354.                         var boneWeights = new BoneWeight[count];
  355.                         for (int i = 0; i < count; i++)
  356.                             boneWeights[i] = aReader.ReadBoneWeight();
  357.                         aTarget.boneWeights = boneWeights;
  358.                         break;
  359.                     case EChunkID.UV0:
  360.                     case EChunkID.UV1:
  361.                     case EChunkID.UV2:
  362.                     case EChunkID.UV3:
  363.                         int uvChannel = chunkID - EChunkID.UV0;
  364.                         componentCount = aReader.ReadByte();
  365.                         if (vector4List == null)
  366.                             vector4List = new List<Vector4>(count);
  367.                         vector4List.Clear();
  368.  
  369.                         if (componentCount == 2)
  370.                         {
  371.                             for (int i = 0; i < count; i++)
  372.                                 vector4List.Add(aReader.ReadVector2());
  373.                         }
  374.                         else if (componentCount == 3)
  375.                         {
  376.                             for (int i = 0; i < count; i++)
  377.                                 vector4List.Add(aReader.ReadVector3());
  378.                         }
  379.                         else if (componentCount == 4)
  380.                         {
  381.                             for (int i = 0; i < count; i++)
  382.                                 vector4List.Add(aReader.ReadVector4());
  383.                         }
  384.                         aTarget.SetUVs(uvChannel, vector4List);
  385.                         break;
  386.                     case EChunkID.Submesh:
  387.                         var topology = (MeshTopology)aReader.ReadByte();
  388.                         int indexCount = aReader.ReadInt32();
  389.                         var indices = new int[indexCount];
  390.                         componentCount = aReader.ReadByte();
  391.                         if (componentCount == 1)
  392.                         {
  393.                             for (int i = 0; i < indexCount; i++)
  394.                                 indices[i] = aReader.ReadByte();
  395.                         }
  396.                         else if (componentCount == 2)
  397.                         {
  398.                             for (int i = 0; i < indexCount; i++)
  399.                                 indices[i] = aReader.ReadUInt16();
  400.                         }
  401.                         else if (componentCount == 4)
  402.                         {
  403.                             for (int i = 0; i < indexCount; i++)
  404.                                 indices[i] = aReader.ReadInt32();
  405.                         }
  406.                         aTarget.SetIndices(indices, topology, subMeshIndex++, false);
  407.                         break;
  408.                     case EChunkID.Bindposes:
  409.                         int bindposesCount = aReader.ReadInt32();
  410.                         var bindposes = new Matrix4x4[bindposesCount];
  411.                         for (int i = 0; i < bindposesCount; i++)
  412.                             bindposes[i] = aReader.ReadMatrix4x4();
  413.                         aTarget.bindposes = bindposes;
  414.                         break;
  415.                     case EChunkID.BlendShape:
  416.                         var blendShapeName = aReader.ReadString();
  417.                         int frameCount = aReader.ReadInt32();
  418.                         if (vector3Array2 == null)
  419.                             vector3Array2 = new Vector3[count];
  420.                         if (vector3Array3 == null)
  421.                             vector3Array3 = new Vector3[count];
  422.                         for (int i = 0; i < frameCount; i++)
  423.                         {
  424.                             float weight = aReader.ReadSingle();
  425.                             for (int n = 0; n < count; n++)
  426.                             {
  427.                                 vector3Array[n] = aReader.ReadVector3();
  428.                                 vector3Array2[n] = aReader.ReadVector3();
  429.                                 vector3Array3[n] = aReader.ReadVector3();
  430.                             }
  431.                             aTarget.AddBlendShapeFrame(blendShapeName, weight, vector3Array, vector3Array2, vector3Array3);
  432.                         }
  433.                         break;
  434.                 }
  435.             }
  436.             return aTarget;
  437.         }
  438.     }
  439.  
  440.  
  441.     public static class BinaryReaderWriterUnityExt
  442.     {
  443.         public static void WriteVector2(this BinaryWriter aWriter, Vector2 aVec)
  444.         {
  445.             aWriter.Write(aVec.x); aWriter.Write(aVec.y);
  446.         }
  447.         public static Vector2 ReadVector2(this BinaryReader aReader)
  448.         {
  449.             return new Vector2(aReader.ReadSingle(), aReader.ReadSingle());
  450.         }
  451.         public static void WriteVector3(this BinaryWriter aWriter, Vector3 aVec)
  452.         {
  453.             aWriter.Write(aVec.x); aWriter.Write(aVec.y); aWriter.Write(aVec.z);
  454.         }
  455.         public static Vector3 ReadVector3(this BinaryReader aReader)
  456.         {
  457.             return new Vector3(aReader.ReadSingle(), aReader.ReadSingle(), aReader.ReadSingle());
  458.         }
  459.         public static void WriteVector4(this BinaryWriter aWriter, Vector4 aVec)
  460.         {
  461.             aWriter.Write(aVec.x); aWriter.Write(aVec.y); aWriter.Write(aVec.z); aWriter.Write(aVec.w);
  462.         }
  463.         public static Vector4 ReadVector4(this BinaryReader aReader)
  464.         {
  465.             return new Vector4(aReader.ReadSingle(), aReader.ReadSingle(), aReader.ReadSingle(), aReader.ReadSingle());
  466.         }
  467.  
  468.         public static void WriteColor32(this BinaryWriter aWriter, Color32 aCol)
  469.         {
  470.             aWriter.Write(aCol.r); aWriter.Write(aCol.g); aWriter.Write(aCol.b); aWriter.Write(aCol.a);
  471.         }
  472.         public static Color32 ReadColor32(this BinaryReader aReader)
  473.         {
  474.             return new Color32(aReader.ReadByte(), aReader.ReadByte(), aReader.ReadByte(), aReader.ReadByte());
  475.         }
  476.  
  477.         public static void WriteMatrix4x4(this BinaryWriter aWriter, Matrix4x4 aMat)
  478.         {
  479.             aWriter.Write(aMat.m00); aWriter.Write(aMat.m01); aWriter.Write(aMat.m02); aWriter.Write(aMat.m03);
  480.             aWriter.Write(aMat.m10); aWriter.Write(aMat.m11); aWriter.Write(aMat.m12); aWriter.Write(aMat.m13);
  481.             aWriter.Write(aMat.m20); aWriter.Write(aMat.m21); aWriter.Write(aMat.m22); aWriter.Write(aMat.m23);
  482.             aWriter.Write(aMat.m30); aWriter.Write(aMat.m31); aWriter.Write(aMat.m32); aWriter.Write(aMat.m33);
  483.         }
  484.         public static Matrix4x4 ReadMatrix4x4(this BinaryReader aReader)
  485.         {
  486.             var m = new Matrix4x4();
  487.             m.m00 = aReader.ReadSingle(); m.m01 = aReader.ReadSingle(); m.m02 = aReader.ReadSingle(); m.m03 = aReader.ReadSingle();
  488.             m.m10 = aReader.ReadSingle(); m.m11 = aReader.ReadSingle(); m.m12 = aReader.ReadSingle(); m.m13 = aReader.ReadSingle();
  489.             m.m20 = aReader.ReadSingle(); m.m21 = aReader.ReadSingle(); m.m22 = aReader.ReadSingle(); m.m23 = aReader.ReadSingle();
  490.             m.m30 = aReader.ReadSingle(); m.m31 = aReader.ReadSingle(); m.m32 = aReader.ReadSingle(); m.m33 = aReader.ReadSingle();
  491.             return m;
  492.         }
  493.  
  494.         public static void WriteBoneWeight(this BinaryWriter aWriter, BoneWeight aWeight)
  495.         {
  496.             aWriter.Write(aWeight.boneIndex0); aWriter.Write(aWeight.weight0);
  497.             aWriter.Write(aWeight.boneIndex1); aWriter.Write(aWeight.weight1);
  498.             aWriter.Write(aWeight.boneIndex2); aWriter.Write(aWeight.weight2);
  499.             aWriter.Write(aWeight.boneIndex3); aWriter.Write(aWeight.weight3);
  500.         }
  501.         public static BoneWeight ReadBoneWeight(this BinaryReader aReader)
  502.         {
  503.             var w = new BoneWeight();
  504.             w.boneIndex0 = aReader.ReadInt32(); w.weight0 = aReader.ReadSingle();
  505.             w.boneIndex1 = aReader.ReadInt32(); w.weight1 = aReader.ReadSingle();
  506.             w.boneIndex2 = aReader.ReadInt32(); w.weight2 = aReader.ReadSingle();
  507.             w.boneIndex3 = aReader.ReadInt32(); w.weight3 = aReader.ReadSingle();
  508.             return w;
  509.         }
  510.     }
  511. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top