Tanoda
MeshSerializer.cs
Go to the documentation of this file.
1namespace B83.MeshTools
2{
3 using System.IO;
4 using System.Collections.Generic;
5 using UnityEngine;
6 using System.Linq;
7
8 [System.Serializable]
9 public class MeshData
10 {
11 [SerializeField, HideInInspector]
12 private byte[] m_Data;
13 private Mesh m_Mesh;
14 public byte[] Data { get{ return m_Data; } }
15 public void SetMesh(Mesh aMesh)
16 {
17 m_Mesh = aMesh;
18 if (aMesh == null)
19 m_Data = null;
20 else
21 m_Data = MeshSerializer.SerializeMesh(m_Mesh);
22 }
23 public Mesh GetMesh()
24 {
25 if (m_Mesh == null && m_Data != null)
26 m_Mesh = MeshSerializer.DeserializeMesh(m_Data);
27 return m_Mesh;
28 }
29 }
30
31
32 public static class MeshSerializer
33 {
34 /*
35 * Structure:
36 * - Magic string "Mesh" (4 bytes)
37 * - vertex count [int] (4 bytes)
38 * - submesh count [int] (4 bytes)
39 * - vertices [array of Vector3]
40 *
41 * - additional chunks:
42 * [vertex attributes]
43 * - Name (name of the Mesh object)
44 * - Normals [array of Vector3]
45 * - Tangents [array of Vector4]
46 * - Colors [array of Color32]
47 * - UV0-4 [
48 * - component count[byte](2/3/4)
49 * - array of Vector2/3/4
50 * ]
51 * - BoneWeights [array of 4x int+float pair]
52 *
53 * [other data]
54 * - Submesh [
55 * - topology[byte]
56 * - count[int]
57 * - component size[byte](1/2/4)
58 * - array of byte/ushort/int
59 * ]
60 * - Bindposes [
61 * - count[int]
62 * - array of Matrix4x4
63 * ]
64 * - BlendShape [
65 * - Name [string]
66 * - frameCount [int]
67 * - frames [ array of:
68 * - frameWeight [float]
69 * - array of [
70 * - position delta [Vector3]
71 * - normal delta [Vector3]
72 * - tangent delta [Vector3]
73 * ]
74 * ]
75 * ]
76 */
77 private enum EChunkID : byte
78 {
79 End,
80 Name,
81 Normals,
82 Tangents,
83 Colors,
84 BoneWeights,
85 UV0, UV1, UV2, UV3,
86 Submesh,
87 Bindposes,
88 BlendShape,
89 }
90 const uint m_Magic = 0x6873654D; // "Mesh"
91
92 public static byte[] SerializeMesh(Mesh aMesh)
93 {
94 using (var stream = new MemoryStream())
95 {
96 SerializeMesh(stream, aMesh);
97 return stream.ToArray();
98 }
99 }
100 public static void SerializeMesh(MemoryStream aStream, Mesh aMesh)
101 {
102 using (var writer = new BinaryWriter(aStream))
103 SerializeMesh(writer, aMesh);
104 }
105 public static void SerializeMesh(BinaryWriter aWriter, Mesh aMesh)
106 {
107 aWriter.Write(m_Magic);
108 var vertices = aMesh.vertices;
109 int count = vertices.Length;
110 int subMeshCount = aMesh.subMeshCount;
111 aWriter.Write(count);
112 aWriter.Write(subMeshCount);
113 foreach (var v in vertices)
114 aWriter.WriteVector3(v);
115
116 // start of tagged chunks
117 if (!string.IsNullOrEmpty(aMesh.name))
118 {
119 aWriter.Write((byte)EChunkID.Name);
120 aWriter.Write(aMesh.name);
121 }
122 var normals = aMesh.normals;
123 if (normals != null && normals.Length == count)
124 {
125 aWriter.Write((byte)EChunkID.Normals);
126 foreach (var v in normals)
127 aWriter.WriteVector3(v);
128 normals = null;
129 }
130 var tangents = aMesh.tangents;
131 if (tangents != null && tangents.Length == count)
132 {
133 aWriter.Write((byte)EChunkID.Tangents);
134 foreach (var v in tangents)
135 aWriter.WriteVector4(v);
136 tangents = null;
137 }
138 var colors = aMesh.colors32;
139 if (colors != null && colors.Length == count)
140 {
141 aWriter.Write((byte)EChunkID.Colors);
142 foreach (var c in colors)
143 aWriter.WriteColor32(c);
144 colors = null;
145 }
146 var boneWeights = aMesh.boneWeights;
147 if (boneWeights != null && boneWeights.Length == count)
148 {
149 aWriter.Write((byte)EChunkID.BoneWeights);
150 foreach (var w in boneWeights)
151 aWriter.WriteBoneWeight(w);
152 boneWeights = null;
153 }
154 List<Vector4> uvs = new List<Vector4>();
155 for (int i = 0; i < 4; i++)
156 {
157 uvs.Clear();
158 aMesh.GetUVs(i, uvs);
159 if (uvs.Count == count)
160 {
161 aWriter.Write((byte)((byte)EChunkID.UV0 + i));
162 byte channelCount = 2;
163 foreach (var uv in uvs)
164 {
165 if (uv.z != 0f)
166 channelCount = 3;
167 if (uv.w != 0f)
168 {
169 channelCount = 4;
170 break;
171 }
172 }
173 aWriter.Write(channelCount);
174 if (channelCount == 2)
175 foreach (var uv in uvs)
176 aWriter.WriteVector2(uv);
177 else if (channelCount == 3)
178 foreach (var uv in uvs)
179 aWriter.WriteVector3(uv);
180 else
181 foreach (var uv in uvs)
182 aWriter.WriteVector4(uv);
183 }
184 }
185 List<int> indices = new List<int>(count * 3);
186 for (int i = 0; i < subMeshCount; i++)
187 {
188 indices.Clear();
189 aMesh.GetIndices(indices, i);
190 if (indices.Count > 0)
191 {
192 aWriter.Write((byte)EChunkID.Submesh);
193 aWriter.Write((byte)aMesh.GetTopology(i));
194 aWriter.Write(indices.Count);
195 var max = indices.Max();
196 if (max < 256)
197 {
198 aWriter.Write((byte)1);
199 foreach (var index in indices)
200 aWriter.Write((byte)index);
201 }
202 else if (max < 65536)
203 {
204 aWriter.Write((byte)2);
205 foreach (var index in indices)
206 aWriter.Write((ushort)index);
207 }
208 else
209 {
210 aWriter.Write((byte)4);
211 foreach (var index in indices)
212 aWriter.Write(index);
213 }
214 }
215 }
216 var bindposes = aMesh.bindposes;
217 if (bindposes != null && bindposes.Length > 0)
218 {
219 aWriter.Write((byte)EChunkID.Bindposes);
220 aWriter.Write(bindposes.Length);
221 foreach (var b in bindposes)
222 aWriter.WriteMatrix4x4(b);
223 bindposes = null;
224 }
225 int blendShapeCount = aMesh.blendShapeCount;
226 if (blendShapeCount > 0)
227 {
228 var blendVerts = new Vector3[count];
229 var blendNormals = new Vector3[count];
230 var blendTangents = new Vector3[count];
231 for (int i = 0; i < blendShapeCount; i++)
232 {
233 aWriter.Write((byte)EChunkID.BlendShape);
234 aWriter.Write(aMesh.GetBlendShapeName(i));
235 var frameCount = aMesh.GetBlendShapeFrameCount(i);
236 aWriter.Write(frameCount);
237 for (int n = 0; n < frameCount; n++)
238 {
239 aMesh.GetBlendShapeFrameVertices(i, n, blendVerts, blendNormals, blendTangents);
240 aWriter.Write(aMesh.GetBlendShapeFrameWeight(i, n));
241 for (int k = 0; k < count; k++)
242 {
243 aWriter.WriteVector3(blendVerts[k]);
244 aWriter.WriteVector3(blendNormals[k]);
245 aWriter.WriteVector3(blendTangents[k]);
246 }
247 }
248 }
249 }
250 aWriter.Write((byte)EChunkID.End);
251 }
252
253
254 public static Mesh DeserializeMesh(byte[] aData, Mesh aTarget = null)
255 {
256 using (var stream = new MemoryStream(aData))
257 return DeserializeMesh(stream, aTarget);
258 }
259 public static Mesh DeserializeMesh(MemoryStream aStream, Mesh aTarget = null)
260 {
261 using (var reader = new BinaryReader(aStream))
262 return DeserializeMesh(reader, aTarget);
263 }
264 public static Mesh DeserializeMesh(BinaryReader aReader, Mesh aTarget = null)
265 {
266 if (aReader.BaseStream == null || aReader.BaseStream.Length == 0)
267 return null;
268
269 if (aReader.ReadUInt32() != m_Magic)
270 return null;
271 if (aTarget == null)
272 aTarget = new Mesh();
273 aTarget.Clear();
274 aTarget.ClearBlendShapes();
275 int count = aReader.ReadInt32();
276 if (count > 65534)
277 aTarget.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
278 int subMeshCount = aReader.ReadInt32();
279 Vector3[] vector3Array = new Vector3[count];
280 Vector3[] vector3Array2 = null;
281 Vector3[] vector3Array3 = null;
282 List<Vector4> vector4List = null;
283 for (int i = 0; i < count; i++)
284 vector3Array[i] = aReader.ReadVector3();
285 aTarget.vertices = vector3Array;
286 aTarget.subMeshCount = subMeshCount;
287 int subMeshIndex = 0;
288 byte componentCount = 0;
289
290 // reading chunks
291 var stream = aReader.BaseStream;
292 while ((stream.CanSeek && stream.Position < stream.Length) || stream.CanRead)
293 {
294 var chunkID = (EChunkID)aReader.ReadByte();
295 if (chunkID == EChunkID.End)
296 break;
297 switch (chunkID)
298 {
299 case EChunkID.Name:
300 aTarget.name = aReader.ReadString();
301 break;
302 case EChunkID.Normals:
303 for (int i = 0; i < count; i++)
304 vector3Array[i] = aReader.ReadVector3();
305 aTarget.normals = vector3Array;
306 break;
307 case EChunkID.Tangents:
308 if (vector4List == null)
309 vector4List = new List<Vector4>(count);
310 vector4List.Clear();
311 for (int i = 0; i < count; i++)
312 vector4List.Add(aReader.ReadVector4());
313 aTarget.SetTangents(vector4List);
314 break;
315 case EChunkID.Colors:
316 var colors = new Color32[count];
317 for (int i = 0; i < count; i++)
318 colors[i] = aReader.ReadColor32();
319 aTarget.colors32 = colors;
320 break;
321 case EChunkID.BoneWeights:
322 var boneWeights = new BoneWeight[count];
323 for (int i = 0; i < count; i++)
324 boneWeights[i] = aReader.ReadBoneWeight();
325 aTarget.boneWeights = boneWeights;
326 break;
327 case EChunkID.UV0:
328 case EChunkID.UV1:
329 case EChunkID.UV2:
330 case EChunkID.UV3:
331 int uvChannel = chunkID - EChunkID.UV0;
332 componentCount = aReader.ReadByte();
333 if (vector4List == null)
334 vector4List = new List<Vector4>(count);
335 vector4List.Clear();
336
337 if (componentCount == 2)
338 {
339 for (int i = 0; i < count; i++)
340 vector4List.Add(aReader.ReadVector2());
341 }
342 else if (componentCount == 3)
343 {
344 for (int i = 0; i < count; i++)
345 vector4List.Add(aReader.ReadVector3());
346 }
347 else if (componentCount == 4)
348 {
349 for (int i = 0; i < count; i++)
350 vector4List.Add(aReader.ReadVector4());
351 }
352 aTarget.SetUVs(uvChannel, vector4List);
353 break;
354 case EChunkID.Submesh:
355 var topology = (MeshTopology)aReader.ReadByte();
356 int indexCount = aReader.ReadInt32();
357 var indices = new int[indexCount];
358 componentCount = aReader.ReadByte();
359 if (componentCount == 1)
360 {
361 for (int i = 0; i < indexCount; i++)
362 indices[i] = aReader.ReadByte();
363 }
364 else if (componentCount == 2)
365 {
366 for (int i = 0; i < indexCount; i++)
367 indices[i] = aReader.ReadUInt16();
368 }
369 else if (componentCount == 4)
370 {
371 for (int i = 0; i < indexCount; i++)
372 indices[i] = aReader.ReadInt32();
373 }
374 aTarget.SetIndices(indices, topology, subMeshIndex++, false);
375 break;
376 case EChunkID.Bindposes:
377 int bindposesCount = aReader.ReadInt32();
378 var bindposes = new Matrix4x4[bindposesCount];
379 for (int i = 0; i < bindposesCount; i++)
380 bindposes[i] = aReader.ReadMatrix4x4();
381 aTarget.bindposes = bindposes;
382 break;
383 case EChunkID.BlendShape:
384 var blendShapeName = aReader.ReadString();
385 int frameCount = aReader.ReadInt32();
386 if (vector3Array2 == null)
387 vector3Array2 = new Vector3[count];
388 if (vector3Array3 == null)
389 vector3Array3 = new Vector3[count];
390 for (int i = 0; i < frameCount; i++)
391 {
392 float weight = aReader.ReadSingle();
393 for (int n = 0; n < count; n++)
394 {
395 vector3Array[n] = aReader.ReadVector3();
396 vector3Array2[n] = aReader.ReadVector3();
397 vector3Array3[n] = aReader.ReadVector3();
398 }
399 aTarget.AddBlendShapeFrame(blendShapeName, weight, vector3Array, vector3Array2, vector3Array3);
400 }
401 break;
402 }
403 }
404 return aTarget;
405 }
406 }
407
408
409 public static class BinaryReaderWriterUnityExt
410 {
411 public static void WriteVector2(this BinaryWriter aWriter, Vector2 aVec)
412 {
413 aWriter.Write(aVec.x); aWriter.Write(aVec.y);
414 }
415 public static Vector2 ReadVector2(this BinaryReader aReader)
416 {
417 return new Vector2(aReader.ReadSingle(), aReader.ReadSingle());
418 }
419 public static void WriteVector3(this BinaryWriter aWriter, Vector3 aVec)
420 {
421 aWriter.Write(aVec.x); aWriter.Write(aVec.y); aWriter.Write(aVec.z);
422 }
423 public static Vector3 ReadVector3(this BinaryReader aReader)
424 {
425 return new Vector3(aReader.ReadSingle(), aReader.ReadSingle(), aReader.ReadSingle());
426 }
427 public static void WriteVector4(this BinaryWriter aWriter, Vector4 aVec)
428 {
429 aWriter.Write(aVec.x); aWriter.Write(aVec.y); aWriter.Write(aVec.z); aWriter.Write(aVec.w);
430 }
431 public static Vector4 ReadVector4(this BinaryReader aReader)
432 {
433 return new Vector4(aReader.ReadSingle(), aReader.ReadSingle(), aReader.ReadSingle(), aReader.ReadSingle());
434 }
435
436 public static void WriteColor32(this BinaryWriter aWriter, Color32 aCol)
437 {
438 aWriter.Write(aCol.r); aWriter.Write(aCol.g); aWriter.Write(aCol.b); aWriter.Write(aCol.a);
439 }
440 public static Color32 ReadColor32(this BinaryReader aReader)
441 {
442 return new Color32(aReader.ReadByte(), aReader.ReadByte(), aReader.ReadByte(), aReader.ReadByte());
443 }
444
445 public static void WriteMatrix4x4(this BinaryWriter aWriter, Matrix4x4 aMat)
446 {
447 aWriter.Write(aMat.m00); aWriter.Write(aMat.m01); aWriter.Write(aMat.m02); aWriter.Write(aMat.m03);
448 aWriter.Write(aMat.m10); aWriter.Write(aMat.m11); aWriter.Write(aMat.m12); aWriter.Write(aMat.m13);
449 aWriter.Write(aMat.m20); aWriter.Write(aMat.m21); aWriter.Write(aMat.m22); aWriter.Write(aMat.m23);
450 aWriter.Write(aMat.m30); aWriter.Write(aMat.m31); aWriter.Write(aMat.m32); aWriter.Write(aMat.m33);
451 }
452 public static Matrix4x4 ReadMatrix4x4(this BinaryReader aReader)
453 {
454 var m = new Matrix4x4();
455 m.m00 = aReader.ReadSingle(); m.m01 = aReader.ReadSingle(); m.m02 = aReader.ReadSingle(); m.m03 = aReader.ReadSingle();
456 m.m10 = aReader.ReadSingle(); m.m11 = aReader.ReadSingle(); m.m12 = aReader.ReadSingle(); m.m13 = aReader.ReadSingle();
457 m.m20 = aReader.ReadSingle(); m.m21 = aReader.ReadSingle(); m.m22 = aReader.ReadSingle(); m.m23 = aReader.ReadSingle();
458 m.m30 = aReader.ReadSingle(); m.m31 = aReader.ReadSingle(); m.m32 = aReader.ReadSingle(); m.m33 = aReader.ReadSingle();
459 return m;
460 }
461
462 public static void WriteBoneWeight(this BinaryWriter aWriter, BoneWeight aWeight)
463 {
464 aWriter.Write(aWeight.boneIndex0); aWriter.Write(aWeight.weight0);
465 aWriter.Write(aWeight.boneIndex1); aWriter.Write(aWeight.weight1);
466 aWriter.Write(aWeight.boneIndex2); aWriter.Write(aWeight.weight2);
467 aWriter.Write(aWeight.boneIndex3); aWriter.Write(aWeight.weight3);
468 }
469 public static BoneWeight ReadBoneWeight(this BinaryReader aReader)
470 {
471 var w = new BoneWeight();
472 w.boneIndex0 = aReader.ReadInt32(); w.weight0 = aReader.ReadSingle();
473 w.boneIndex1 = aReader.ReadInt32(); w.weight1 = aReader.ReadSingle();
474 w.boneIndex2 = aReader.ReadInt32(); w.weight2 = aReader.ReadSingle();
475 w.boneIndex3 = aReader.ReadInt32(); w.weight3 = aReader.ReadSingle();
476 return w;
477 }
478 }
479}
void SetMesh(Mesh aMesh)