Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Resolved Serializing a SkinnedMeshRender and loading with addressables

Discussion in 'Scripting' started by standstilldigitalmedia, Feb 10, 2023.

  1. standstilldigitalmedia

    standstilldigitalmedia

    Joined:
    Dec 26, 2021
    Posts:
    6
    I am trying to serialize a SkinnedMeshRenderer using code found at https://forum.unity.com/threads/serializing-skinned-mesh-renderer.1162739/ Using the code in an editor script as it is works perfectly for me but I want to be able to use the created assets with addressables. I have Googled everything I can think of for about a week and I keep hitting dead ends. Lots of suggestions to write it out to file with the .byte extension and read it back into a TextAsset.


    Code (csharp):
    1.  
    2. //write file
    3. buf.Close();
    4. stream.Close();
    5. File.WriteAllBytes("Assets/Resources/test.bytes", stream.ToArray());
    6.  
    7. //read file
    8. TextAsset textAsset = Resources.Load("Assets/Resources/test.bytes") as TextAsset;
    9. MemoryStream memStream = new MemoryStream(textAsset.bytes);
    10. BinaryReader buf = new BinaryReader(memStream);
    11.  

    This produces a null reference exception at MemoryStream memStream = new MemoryStream(textAsset.bytes);

    I tried to write the byte[] out to a SerlizedProperty of a ScriptableObject. The code runs without errors but there is no armature and no visable mesh. When I check the target SkinnedMeshRender, it has an error in the inspector.

    The assigned mesh is missing either bone weights with bindpose, or blend shapes. This might cause the mesh not to render in the Player. If your mesh does not have either bone weights with bind pose, or blend shapes, use a Mesh Renerer instead of Skinned Mesh Renderer.

    Code (csharp):
    1.  
    2. //writing
    3. AWOC.AWOCSo tempMesh = (AWOC.AWOCSo)ScriptableObject.CreateInstance(typeof(AWOC.AWOCSo));
    4.         SerializedObject tempMeshSO = new SerializedObject(tempMesh);
    5.         SerializedProperty testByteArray = tempMeshSO.FindProperty("testByteArray");
    6.         byte[] tempByte = new byte[stream.ToArray().Length];
    7.         testByteArray.arraySize = stream.ToArray().Length;
    8.         for (int a = 0; a < stream.ToArray().Length; a++)
    9.         {
    10.             testByteArray.GetArrayElementAtIndex(a).intValue = tempByte[a];
    11.         }
    12.         tempMeshSO.ApplyModifiedProperties();
    13.         AssetDatabase.CreateAsset(tempMesh, "Assets/Resources/test.asset");
    14.         AssetDatabase.SaveAssets();
    15.         AssetDatabase.Refresh();
    16.         buf.Close();
    17.         stream.Close();
    18.  
    19. //reading
    20. AWOC.AWOCSo newMesh = (AWOC.AWOCSo)AssetDatabase.LoadAssetAtPath("Assets/Resources/test.asset", typeof(AWOC.AWOCSo));
    21.         SerializedObject newMeshSO = new SerializedObject(newMesh);
    22.         SerializedProperty meshArray = newMeshSO.FindProperty("testByteArray");
    23.         byte[] tempByte = new byte[meshArray.arraySize];
    24.         for(int a = 0; a < meshArray.arraySize; a++)
    25.         {
    26.             tempByte[a] = (byte)meshArray.GetArrayElementAtIndex(a).intValue;
    27.         }
    28.         MemoryStream memStream = new MemoryStream(tempByte);
    29.         BinaryReader buf = new BinaryReader(memStream);
    30.  
    I spent a great deal of time converting VGS_SkinnedMeshUtility.cs to read and write Scriptable Objects but it takes several minutes to read a single SkinnedMeshRenderer and that is not acceptable.

    Code (csharp):
    1.  
    2. class SkeletonGO
    3.  
    4. {
    5.  
    6. public string name;
    7.  
    8. public string parentName;
    9.  
    10. public Transform transform;
    11.  
    12.  
    13.  
    14. public SkeletonGO(string name, string parentName, Transform transform)
    15.  
    16. {
    17.  
    18. this.name = name;
    19.  
    20. this.parentName = parentName;
    21.  
    22. this.transform = transform;
    23.  
    24. }
    25.  
    26. }
    27.  
    28.  
    29.  
    30. //a helper to write Vector2[] to a Vector2[] SerializedProperty
    31.  
    32. static void WriteVec2Array(SerializedProperty dest, Vector2[] source)
    33.  
    34. {
    35.  
    36. for (int a = 0; a < source.Length; a++)
    37.  
    38. {
    39.  
    40. dest.InsertArrayElementAtIndex(a);
    41.  
    42. dest.GetArrayElementAtIndex(a).vector2Value = source[a];
    43.  
    44. }
    45.  
    46. }
    47.  
    48. static Vector2[] ReadVec2Array(SerializedProperty source)
    49.  
    50. {
    51.  
    52. int arraySize = source.arraySize;
    53.  
    54. Vector2[] vector2Array = new Vector2[arraySize];
    55.  
    56. for (int a = 0; a < arraySize; a++)
    57.  
    58. {
    59.  
    60. vector2Array[a] = source.GetArrayElementAtIndex(a).vector2Value;
    61.  
    62. }
    63.  
    64. return vector2Array;
    65.  
    66. }
    67.  
    68. //a helper to write Vector3[] to a Vector3[] SerializedProperty
    69.  
    70. static void WriteVec3Array(SerializedProperty dest, Vector3[] source)
    71.  
    72. {
    73.  
    74. for (int a = 0; a < source.Length; a++)
    75.  
    76. {
    77.  
    78. dest.InsertArrayElementAtIndex(a);
    79.  
    80. dest.GetArrayElementAtIndex(a).vector3Value = source[a];
    81.  
    82. }
    83.  
    84. }
    85.  
    86. static Vector3[] ReadVec3Array(SerializedProperty source)
    87.  
    88. {
    89.  
    90. int arraySize = source.arraySize;
    91.  
    92. Vector3[] vector3Array = new Vector3[arraySize];
    93.  
    94. for (int a = 0; a < arraySize; a++)
    95.  
    96. {
    97.  
    98. vector3Array[a] = source.GetArrayElementAtIndex(a).vector3Value;
    99.  
    100. }
    101.  
    102. return vector3Array;
    103.  
    104. }
    105.  
    106. //a helper to write Vector4[] to a Vector4[] SerializedProperty
    107.  
    108. static void WriteVec4Array(SerializedProperty dest, Vector4[] source)
    109.  
    110. {
    111.  
    112. for (int a = 0; a < source.Length; a++)
    113.  
    114. {
    115.  
    116. dest.InsertArrayElementAtIndex(a);
    117.  
    118. dest.GetArrayElementAtIndex(a).vector4Value = source[a];
    119.  
    120. }
    121.  
    122. }
    123.  
    124. static Vector4[] ReadVec4Array(SerializedProperty source)
    125.  
    126. {
    127.  
    128. int arraySize = source.arraySize;
    129.  
    130. Vector4[] vector4Array = new Vector4[arraySize];
    131.  
    132. for (int a = 0; a < arraySize; a++)
    133.  
    134. {
    135.  
    136. vector4Array[a] = source.GetArrayElementAtIndex(a).vector4Value;
    137.  
    138. }
    139.  
    140. return vector4Array;
    141.  
    142. }
    143.  
    144. static void WriteSubmeshTris(SerializedObject trisSO, SkinnedMeshRenderer sourceSMR)
    145.  
    146. {
    147.  
    148. SerializedProperty tris = trisSO.FindProperty("subMeshTris");
    149.  
    150. //the first element in the array holds the submesh count
    151.  
    152. //the rest of the array will be populated with triangle information in the loop below
    153.  
    154. tris.InsertArrayElementAtIndex(0);
    155.  
    156. tris.GetArrayElementAtIndex(0).intValue = sourceSMR.sharedMesh.subMeshCount;
    157.  
    158.  
    159.  
    160. int a = 1;
    161.  
    162. for (int b = 0; b < sourceSMR.sharedMesh.subMeshCount; b++)
    163.  
    164. {
    165.  
    166. int[] tempTris = sourceSMR.sharedMesh.GetTriangles(b);
    167.  
    168. tris.InsertArrayElementAtIndex(a);
    169.  
    170. tris.GetArrayElementAtIndex(a).intValue = tempTris.Length;
    171.  
    172. a++;
    173.  
    174. foreach (int tri in tempTris)
    175.  
    176. {
    177.  
    178. tris.InsertArrayElementAtIndex(a);
    179.  
    180. tris.GetArrayElementAtIndex(a).intValue = tri;
    181.  
    182. a++;
    183.  
    184. }
    185.  
    186. }
    187.  
    188. }
    189.  
    190. static void ReadSubmeshTris(SerializedObject trisSO, Mesh sourceMesh)
    191.  
    192. {
    193.  
    194. SerializedProperty tris = trisSO.FindProperty("subMeshTris");
    195.  
    196. int trisCount = tris.GetArrayElementAtIndex(0).intValue;
    197.  
    198.  
    199.  
    200. int a = 1;
    201.  
    202. for (int b = 0; b < trisCount; b++)
    203.  
    204. {
    205.  
    206. int tempTrisCount = tris.GetArrayElementAtIndex(a).intValue;
    207.  
    208. int[] subMeshTriIndex = new int[tempTrisCount];
    209.  
    210. a++;
    211.  
    212.  
    213.  
    214. for (int c = 0; c < tempTrisCount; c++)
    215.  
    216. {
    217.  
    218. subMeshTriIndex[c] = tris.GetArrayElementAtIndex(a).intValue;
    219.  
    220. a++;
    221.  
    222. }
    223.  
    224.  
    225.  
    226. sourceMesh.SetTriangles(subMeshTriIndex, b);
    227.  
    228. }
    229.  
    230. }
    231.  
    232. static void WriteBoneWeights(SerializedObject boneWeightsSo, SkinnedMeshRenderer sourceSMR)
    233.  
    234. {
    235.  
    236. SerializedProperty boneIndexes = boneWeightsSo.FindProperty("boneIndexes");
    237.  
    238. SerializedProperty boneWeights = boneWeightsSo.FindProperty("boneWeights");
    239.  
    240.  
    241.  
    242. boneIndexes.InsertArrayElementAtIndex(0);
    243.  
    244. boneIndexes.GetArrayElementAtIndex(0).intValue = sourceSMR.sharedMesh.boneWeights.Length;
    245.  
    246.  
    247.  
    248. boneWeights.InsertArrayElementAtIndex(0);
    249.  
    250. boneWeights.GetArrayElementAtIndex(0).floatValue = sourceSMR.sharedMesh.boneWeights.Length;
    251.  
    252.  
    253.  
    254. int c = 1;
    255.  
    256. foreach (BoneWeight boneWeight in sourceSMR.sharedMesh.boneWeights)
    257.  
    258. {
    259.  
    260. boneIndexes.InsertArrayElementAtIndex(c);
    261.  
    262. boneIndexes.GetArrayElementAtIndex(c).intValue = boneWeight.boneIndex0;
    263.  
    264.  
    265.  
    266. boneWeights.InsertArrayElementAtIndex(c);
    267.  
    268. boneWeights.GetArrayElementAtIndex(c).floatValue = boneWeight.weight0;
    269.  
    270. c++;
    271.  
    272.  
    273.  
    274. boneIndexes.InsertArrayElementAtIndex(c);
    275.  
    276. boneIndexes.GetArrayElementAtIndex(c).intValue = boneWeight.boneIndex1;
    277.  
    278.  
    279.  
    280. boneWeights.InsertArrayElementAtIndex(c);
    281.  
    282. boneWeights.GetArrayElementAtIndex(c).floatValue = boneWeight.weight1;
    283.  
    284. c++;
    285.  
    286.  
    287.  
    288. boneIndexes.InsertArrayElementAtIndex(c);
    289.  
    290. boneIndexes.GetArrayElementAtIndex(c).intValue = boneWeight.boneIndex2;
    291.  
    292.  
    293.  
    294. boneWeights.InsertArrayElementAtIndex(c);
    295.  
    296. boneWeights.GetArrayElementAtIndex(c).floatValue = boneWeight.weight2;
    297.  
    298. c++;
    299.  
    300.  
    301.  
    302. boneIndexes.InsertArrayElementAtIndex(c);
    303.  
    304. boneIndexes.GetArrayElementAtIndex(c).intValue = boneWeight.boneIndex3;
    305.  
    306.  
    307.  
    308. boneWeights.InsertArrayElementAtIndex(c);
    309.  
    310. boneWeights.GetArrayElementAtIndex(c).floatValue = boneWeight.weight3;
    311.  
    312. c++;
    313.  
    314. }
    315.  
    316. }
    317.  
    318. static void ReadBoneWeights(SerializedObject boneWeightsSo, Mesh sourceMesh)
    319.  
    320. {
    321.  
    322. SerializedProperty boneIndexes = boneWeightsSo.FindProperty("boneIndexes");
    323.  
    324. SerializedProperty pboneWeights = boneWeightsSo.FindProperty("boneWeights");
    325.  
    326.  
    327.  
    328. int weightCount = boneIndexes.GetArrayElementAtIndex(0).intValue;
    329.  
    330. BoneWeight[] boneWeights = new BoneWeight[weightCount];
    331.  
    332.  
    333.  
    334. int a = 1;
    335.  
    336. for (int i = 0; i < weightCount; i++)
    337.  
    338. {
    339.  
    340. boneWeights[i].boneIndex0 = boneIndexes.GetArrayElementAtIndex(a).intValue;
    341.  
    342. boneWeights[i].weight0 = pboneWeights.GetArrayElementAtIndex(a).floatValue;
    343.  
    344. a++;
    345.  
    346. boneWeights[i].boneIndex1 = boneIndexes.GetArrayElementAtIndex(a).intValue;
    347.  
    348. boneWeights[i].weight1 = pboneWeights.GetArrayElementAtIndex(a).floatValue;
    349.  
    350. a++;
    351.  
    352. boneWeights[i].boneIndex2 = boneIndexes.GetArrayElementAtIndex(a).intValue;
    353.  
    354. boneWeights[i].weight2 = pboneWeights.GetArrayElementAtIndex(a).floatValue;
    355.  
    356. a++;
    357.  
    358. boneWeights[i].boneIndex3 = boneIndexes.GetArrayElementAtIndex(a).intValue;
    359.  
    360. boneWeights[i].weight3 = pboneWeights.GetArrayElementAtIndex(a).floatValue;
    361.  
    362. a++;
    363.  
    364. }
    365.  
    366.  
    367.  
    368. sourceMesh.boneWeights = boneWeights;
    369.  
    370. }
    371.  
    372. static void WriteUVs(SerializedObject uvSo, SkinnedMeshRenderer sourceSMR)
    373.  
    374. {
    375.  
    376. SerializedProperty uv = uvSo.FindProperty("uv");
    377.  
    378. SerializedProperty uv2 = uvSo.FindProperty("uv2");
    379.  
    380. SerializedProperty uv3 = uvSo.FindProperty("uv3");
    381.  
    382. SerializedProperty uv4 = uvSo.FindProperty("uv4");
    383.  
    384. SerializedProperty uv5 = uvSo.FindProperty("uv5");
    385.  
    386. SerializedProperty uv6 = uvSo.FindProperty("uv6");
    387.  
    388. SerializedProperty uv7 = uvSo.FindProperty("uv7");
    389.  
    390. SerializedProperty uv8 = uvSo.FindProperty("uv8");
    391.  
    392.  
    393.  
    394. WriteVec2Array(uv, sourceSMR.sharedMesh.uv);
    395.  
    396. WriteVec2Array(uv2, sourceSMR.sharedMesh.uv2);
    397.  
    398. WriteVec2Array(uv3, sourceSMR.sharedMesh.uv3);
    399.  
    400. WriteVec2Array(uv4, sourceSMR.sharedMesh.uv4);
    401.  
    402. WriteVec2Array(uv5, sourceSMR.sharedMesh.uv5);
    403.  
    404. WriteVec2Array(uv6, sourceSMR.sharedMesh.uv6);
    405.  
    406. WriteVec2Array(uv7, sourceSMR.sharedMesh.uv7);
    407.  
    408. WriteVec2Array(uv8, sourceSMR.sharedMesh.uv8);
    409.  
    410. }
    411.  
    412. static void ReadUVs(SerializedObject uvSo, Mesh sourceMesh)
    413.  
    414. {
    415.  
    416. SerializedProperty uv = uvSo.FindProperty("uv");
    417.  
    418. SerializedProperty uv2 = uvSo.FindProperty("uv2");
    419.  
    420. SerializedProperty uv3 = uvSo.FindProperty("uv3");
    421.  
    422. SerializedProperty uv4 = uvSo.FindProperty("uv4");
    423.  
    424. SerializedProperty uv5 = uvSo.FindProperty("uv5");
    425.  
    426. SerializedProperty uv6 = uvSo.FindProperty("uv6");
    427.  
    428. SerializedProperty uv7 = uvSo.FindProperty("uv7");
    429.  
    430. SerializedProperty uv8 = uvSo.FindProperty("uv8");
    431.  
    432.  
    433.  
    434. sourceMesh.uv = ReadVec2Array(uv);
    435.  
    436. sourceMesh.uv2 = ReadVec2Array(uv2);
    437.  
    438. sourceMesh.uv3 = ReadVec2Array(uv3);
    439.  
    440. sourceMesh.uv4 = ReadVec2Array(uv4);
    441.  
    442. sourceMesh.uv5 = ReadVec2Array(uv5);
    443.  
    444. sourceMesh.uv6 = ReadVec2Array(uv6);
    445.  
    446. sourceMesh.uv7 = ReadVec2Array(uv7);
    447.  
    448. sourceMesh.uv8 = ReadVec2Array(uv8);
    449.  
    450. }
    451.  
    452.  
    453. static void WriteBindPose(SerializedObject poseSo, SkinnedMeshRenderer sourceSMR)
    454.  
    455. {
    456.  
    457. SerializedProperty bindPose = poseSo.FindProperty("bindPose");
    458.  
    459.  
    460.  
    461. bindPose.InsertArrayElementAtIndex(0);
    462.  
    463. bindPose.GetArrayElementAtIndex(0).floatValue = sourceSMR.sharedMesh.bindposes.Length;
    464.  
    465. int d = 1;
    466.  
    467.  
    468.  
    469. foreach (Matrix4x4 bindpose in sourceSMR.sharedMesh.bindposes)
    470.  
    471. {
    472.  
    473. bindPose.InsertArrayElementAtIndex(d);
    474.  
    475. bindPose.GetArrayElementAtIndex(d).floatValue = bindpose.m00;
    476.  
    477. d++;
    478.  
    479.  
    480.  
    481. bindPose.InsertArrayElementAtIndex(d);
    482.  
    483. bindPose.GetArrayElementAtIndex(d).floatValue = bindpose.m01;
    484.  
    485. d++;
    486.  
    487.  
    488.  
    489. bindPose.InsertArrayElementAtIndex(d);
    490.  
    491. bindPose.GetArrayElementAtIndex(d).floatValue = bindpose.m02;
    492.  
    493. d++;
    494.  
    495.  
    496.  
    497. bindPose.InsertArrayElementAtIndex(d);
    498.  
    499. bindPose.GetArrayElementAtIndex(d).floatValue = bindpose.m03;
    500.  
    501. d++;
    502.  
    503.  
    504.  
    505. bindPose.InsertArrayElementAtIndex(d);
    506.  
    507. bindPose.GetArrayElementAtIndex(d).floatValue = bindpose.m10;
    508.  
    509. d++;
    510.  
    511.  
    512.  
    513. bindPose.InsertArrayElementAtIndex(d);
    514.  
    515. bindPose.GetArrayElementAtIndex(d).floatValue = bindpose.m11;
    516.  
    517. d++;
    518.  
    519.  
    520.  
    521. bindPose.InsertArrayElementAtIndex(d);
    522.  
    523. bindPose.GetArrayElementAtIndex(d).floatValue = bindpose.m12;
    524.  
    525. d++;
    526.  
    527.  
    528.  
    529. bindPose.InsertArrayElementAtIndex(d);
    530.  
    531. bindPose.GetArrayElementAtIndex(d).floatValue = bindpose.m13;
    532.  
    533. d++;
    534.  
    535.  
    536.  
    537. bindPose.InsertArrayElementAtIndex(d);
    538.  
    539. bindPose.GetArrayElementAtIndex(d).floatValue = bindpose.m20;
    540.  
    541. d++;
    542.  
    543.  
    544.  
    545. bindPose.InsertArrayElementAtIndex(d);
    546.  
    547. bindPose.GetArrayElementAtIndex(d).floatValue = bindpose.m21;
    548.  
    549. d++;
    550.  
    551.  
    552.  
    553. bindPose.InsertArrayElementAtIndex(d);
    554.  
    555. bindPose.GetArrayElementAtIndex(d).floatValue = bindpose.m22;
    556.  
    557. d++;
    558.  
    559.  
    560.  
    561. bindPose.InsertArrayElementAtIndex(d);
    562.  
    563. bindPose.GetArrayElementAtIndex(d).floatValue = bindpose.m23;
    564.  
    565. d++;
    566.  
    567.  
    568.  
    569. bindPose.InsertArrayElementAtIndex(d);
    570.  
    571. bindPose.GetArrayElementAtIndex(d).floatValue = bindpose.m30;
    572.  
    573. d++;
    574.  
    575.  
    576.  
    577. bindPose.InsertArrayElementAtIndex(d);
    578.  
    579. bindPose.GetArrayElementAtIndex(d).floatValue = bindpose.m31;
    580.  
    581. d++;
    582.  
    583.  
    584.  
    585. bindPose.InsertArrayElementAtIndex(d);
    586.  
    587. bindPose.GetArrayElementAtIndex(d).floatValue = bindpose.m32;
    588.  
    589. d++;
    590.  
    591.  
    592.  
    593. bindPose.InsertArrayElementAtIndex(d);
    594.  
    595. bindPose.GetArrayElementAtIndex(d).floatValue = bindpose.m33;
    596.  
    597. d++;
    598.  
    599. }
    600.  
    601. }
    602.  
    603. static void ReadBindPose(SerializedObject poseSo, Mesh sourceMesh)
    604.  
    605. {
    606.  
    607. SerializedProperty bindPose = poseSo.FindProperty("bindPose");
    608.  
    609. float poseLength = bindPose.GetArrayElementAtIndex(0).floatValue;
    610.  
    611. Matrix4x4[] bindposes = new Matrix4x4[(int)poseLength];
    612.  
    613.  
    614.  
    615. int a = 1;
    616.  
    617. for (int i = 0; i < bindposes.Length; i++)
    618.  
    619. {
    620.  
    621. bindposes[i].m00 = bindPose.GetArrayElementAtIndex(a).floatValue;
    622.  
    623. a++;
    624.  
    625. bindposes[i].m01 = bindPose.GetArrayElementAtIndex(a).floatValue;
    626.  
    627. a++;
    628.  
    629. bindposes[i].m02 = bindPose.GetArrayElementAtIndex(a).floatValue;
    630.  
    631. a++;
    632.  
    633. bindposes[i].m03 = bindPose.GetArrayElementAtIndex(a).floatValue;
    634.  
    635. a++;
    636.  
    637. bindposes[i].m10 = bindPose.GetArrayElementAtIndex(a).floatValue;
    638.  
    639. a++;
    640.  
    641. bindposes[i].m11 = bindPose.GetArrayElementAtIndex(a).floatValue;
    642.  
    643. a++;
    644.  
    645. bindposes[i].m12 = bindPose.GetArrayElementAtIndex(a).floatValue;
    646.  
    647. a++;
    648.  
    649. bindposes[i].m13 = bindPose.GetArrayElementAtIndex(a).floatValue;
    650.  
    651. a++;
    652.  
    653. bindposes[i].m20 = bindPose.GetArrayElementAtIndex(a).floatValue;
    654.  
    655. a++;
    656.  
    657. bindposes[i].m21 = bindPose.GetArrayElementAtIndex(a).floatValue;
    658.  
    659. a++;
    660.  
    661. bindposes[i].m22 = bindPose.GetArrayElementAtIndex(a).floatValue;
    662.  
    663. a++;
    664.  
    665. bindposes[i].m23 = bindPose.GetArrayElementAtIndex(a).floatValue;
    666.  
    667. a++;
    668.  
    669. bindposes[i].m30 = bindPose.GetArrayElementAtIndex(a).floatValue;
    670.  
    671. a++;
    672.  
    673. bindposes[i].m31 = bindPose.GetArrayElementAtIndex(a).floatValue;
    674.  
    675. a++;
    676.  
    677. bindposes[i].m32 = bindPose.GetArrayElementAtIndex(a).floatValue;
    678.  
    679. a++;
    680.  
    681. bindposes[i].m33 = bindPose.GetArrayElementAtIndex(a).floatValue;
    682.  
    683. a++;
    684.  
    685. }
    686.  
    687.  
    688.  
    689. sourceMesh.bindposes = bindposes;
    690.  
    691. }
    692.  
    693. static void WriteBounds(SerializedObject boundsSo, SkinnedMeshRenderer sourceSMR)
    694.  
    695. {
    696.  
    697. SerializedProperty boundsSize = boundsSo.FindProperty("boundsSize");
    698.  
    699. SerializedProperty boundsCenter = boundsSo.FindProperty("boundsCenter");
    700.  
    701. SerializedProperty boundsExtents = boundsSo.FindProperty("boundsExtents");
    702.  
    703.  
    704.  
    705. SerializedProperty localBoundsSize = boundsSo.FindProperty("localBoundsSize");
    706.  
    707. SerializedProperty localBoundsCenter = boundsSo.FindProperty("localBoundsCenter");
    708.  
    709. SerializedProperty localBoundsExtents = boundsSo.FindProperty("localBoundsExtents");
    710.  
    711.  
    712.  
    713. boundsSize.vector3Value = sourceSMR.sharedMesh.bounds.size;
    714.  
    715. boundsCenter.vector3Value = sourceSMR.sharedMesh.bounds.center;
    716.  
    717. boundsExtents.vector3Value = sourceSMR.sharedMesh.bounds.extents;
    718.  
    719.  
    720.  
    721. localBoundsSize.vector3Value = sourceSMR.localBounds.size;
    722.  
    723. localBoundsCenter.vector3Value = sourceSMR.localBounds.center;
    724.  
    725. localBoundsExtents.vector3Value = sourceSMR.localBounds.extents;
    726.  
    727. }
    728.  
    729. static void ReadBounds(SerializedObject boundsSo, Mesh sourceMesh, SkinnedMeshRenderer sourceSMR)
    730.  
    731. {
    732.  
    733. SerializedProperty boundsSize = boundsSo.FindProperty("boundsSize");
    734.  
    735. SerializedProperty boundsCenter = boundsSo.FindProperty("boundsCenter");
    736.  
    737. SerializedProperty boundsExtents = boundsSo.FindProperty("boundsExtents");
    738.  
    739.  
    740.  
    741. SerializedProperty localBoundsSize = boundsSo.FindProperty("localBoundsSize");
    742.  
    743. SerializedProperty localBoundsCenter = boundsSo.FindProperty("localBoundsCenter");
    744.  
    745. SerializedProperty localBoundsExtents = boundsSo.FindProperty("localBoundsExtents");
    746.  
    747.  
    748.  
    749. Bounds bounds = new Bounds();
    750.  
    751.  
    752.  
    753. bounds.size = boundsSize.vector3Value;
    754.  
    755. bounds.center = boundsCenter.vector3Value;
    756.  
    757. bounds.extents = boundsExtents.vector3Value;
    758.  
    759.  
    760.  
    761. sourceMesh.bounds = bounds;
    762.  
    763.  
    764.  
    765. bounds = new Bounds();
    766.  
    767.  
    768.  
    769. bounds.size = localBoundsSize.vector3Value;
    770.  
    771. bounds.center = localBoundsCenter.vector3Value;
    772.  
    773. bounds.extents = localBoundsExtents.vector3Value;
    774.  
    775.  
    776.  
    777. sourceSMR.localBounds = bounds;
    778.  
    779.  
    780.  
    781. sourceSMR.sharedMesh = sourceMesh;
    782.  
    783. }
    784.  
    785. public static void WriteAWOCMesh(SerializedObject meshSO, SkinnedMeshRenderer sourceSMR)
    786.  
    787. {
    788.  
    789. SerializedProperty localPosition = meshSO.FindProperty("localPosition");
    790.  
    791. SerializedProperty localRotation = meshSO.FindProperty("localRotation");
    792.  
    793. SerializedProperty localScale = meshSO.FindProperty("localScale");
    794.  
    795.  
    796.  
    797. localPosition.vector3Value = sourceSMR.transform.localPosition;
    798.  
    799. Vector4 tempRot = new Vector4(sourceSMR.transform.localRotation.x, sourceSMR.transform.localRotation.y, sourceSMR.transform.localRotation.z, sourceSMR.transform.localRotation.w);
    800.  
    801. localRotation.vector4Value = tempRot;
    802.  
    803. localScale.vector3Value = sourceSMR.transform.localScale;
    804.  
    805.  
    806. SerializedProperty vertices = meshSO.FindProperty("vertices");
    807.  
    808.  
    809.  
    810. WriteVec3Array(vertices, sourceSMR.sharedMesh.vertices);
    811.  
    812. WriteUVs(meshSO, sourceSMR);
    813.  
    814. WriteSubmeshTris(meshSO, sourceSMR);
    815.  
    816. WriteBoneWeights(meshSO, sourceSMR);
    817.  
    818. WriteBounds(meshSO, sourceSMR);
    819.  
    820. WriteBindPose(meshSO, sourceSMR);
    821.  
    822.  
    823.  
    824. meshSO.ApplyModifiedProperties();
    825.  
    826. }
    827.  
    828. public static void ReadAWOCMesh(SkinnedMeshRenderer targetSMR, SerializedObject meshSO, BoneHelper boneHelper)
    829.  
    830. {
    831.  
    832. SerializedProperty localPosition = meshSO.FindProperty("localPosition");
    833.  
    834. SerializedProperty localRotation = meshSO.FindProperty("localRotation");
    835.  
    836. SerializedProperty localScale = meshSO.FindProperty("localScale");
    837.  
    838.  
    839.  
    840. Vector4 tempRot = localRotation.vector4Value;
    841.  
    842. targetSMR.transform.localPosition = localPosition.vector3Value;
    843.  
    844. targetSMR.transform.localRotation = new Quaternion(tempRot.x, tempRot.y, tempRot.z, tempRot.w);
    845.  
    846. targetSMR.transform.localScale = localScale.vector3Value;
    847.  
    848.  
    849.  
    850. SerializedProperty mvertices = meshSO.FindProperty("vertices");
    851.  
    852.  
    853.  
    854. Mesh mesh = new Mesh();
    855.  
    856.  
    857.  
    858. mesh.vertices = ReadVec3Array(mvertices);
    859.  
    860. ReadUVs(meshSO, mesh);
    861.  
    862. ReadSubmeshTris(meshSO, mesh);
    863.  
    864. ReadBoneWeights(meshSO, mesh);
    865.  
    866. ReadBindPose(meshSO, mesh);
    867.  
    868. ReadBounds(meshSO, mesh, targetSMR);
    869.  
    870.  
    871.  
    872. targetSMR.bones = boneHelper.bones;
    873.  
    874. targetSMR.rootBone = boneHelper.rootBoneTransform;
    875.  
    876. }
    877.  
    878. public static void WriteAWOCArmature(SerializedObject armatureSO, SkinnedMeshRenderer sourceSMR, Transform sourceSkeleton)
    879.  
    880. {
    881.  
    882. SerializedProperty rootBoneName = armatureSO.FindProperty("rootBoneName");
    883.  
    884. SerializedProperty bonesLength = armatureSO.FindProperty("bonesLength");
    885.  
    886. SerializedProperty boneNames = armatureSO.FindProperty("boneNames");
    887.  
    888.  
    889. if (rootBoneName == null)
    890.  
    891. {
    892.  
    893. Debug.Log("rootBoneName == null");
    894.  
    895. }
    896.  
    897.  
    898.  
    899. if (rootBoneName.stringValue == null)
    900.  
    901. {
    902.  
    903. Debug.Log("rootBoneName.stringValue == null");
    904.  
    905. }
    906.  
    907.  
    908.  
    909.  
    910.  
    911. if (sourceSMR.rootBone == null)
    912.  
    913. Debug.Log("sourceSMR.rootBone == null");
    914.  
    915.  
    916.  
    917. if (sourceSMR.rootBone.name == null)
    918.  
    919. Debug.Log("sourceSMR.rootBone.name == null");
    920.  
    921.  
    922.  
    923. rootBoneName.stringValue = sourceSMR.rootBone != null ? sourceSMR.rootBone.name : "";
    924.  
    925. bonesLength.intValue = sourceSMR.bones.Length;
    926.  
    927.  
    928.  
    929. int boneNameCount = 0;
    930.  
    931. foreach (Transform t in sourceSMR.bones)
    932.  
    933. {
    934.  
    935. boneNames.InsertArrayElementAtIndex(boneNameCount);
    936.  
    937. boneNames.GetArrayElementAtIndex(boneNameCount).stringValue = t.name;
    938.  
    939. boneNameCount++;
    940.  
    941. }
    942.  
    943.  
    944.  
    945. SerializedProperty skeletonLength = armatureSO.FindProperty("skeletonLength");
    946.  
    947. SerializedProperty skeletonNames = armatureSO.FindProperty("skeletonNames");
    948.  
    949. SerializedProperty skeletonParentNames = armatureSO.FindProperty("skeletonParentNames");
    950.  
    951.  
    952.  
    953. SerializedProperty skeletonPositions = armatureSO.FindProperty("skeletonPositions");
    954.  
    955. SerializedProperty skeletonRotations = armatureSO.FindProperty("skeletonRotations");
    956.  
    957. SerializedProperty skeletonScales = armatureSO.FindProperty("skeletonScales");
    958.  
    959.  
    960.  
    961. if (sourceSkeleton != null)
    962.  
    963. {
    964.  
    965. Transform[] skeleton = sourceSkeleton.GetComponentsInChildren<Transform>(true);
    966.  
    967.  
    968.  
    969. skeletonLength.intValue = skeleton.Length;
    970.  
    971.  
    972.  
    973. Dictionary<string, SkeletonGO> skeletonMap = new Dictionary<string, SkeletonGO>();
    974.  
    975.  
    976.  
    977. int transCount = 0;
    978.  
    979. foreach (Transform t in skeleton)
    980.  
    981. {
    982.  
    983. skeletonMap.Add(t.name, new SkeletonGO(t.name, t.Equals(sourceSkeleton) ? string.Empty : skeletonMap[t.parent.name].name, t));
    984.  
    985.  
    986.  
    987. skeletonNames.InsertArrayElementAtIndex(transCount);
    988.  
    989. skeletonNames.GetArrayElementAtIndex(transCount).stringValue = skeletonMap[t.name].name;
    990.  
    991.  
    992.  
    993. skeletonParentNames.InsertArrayElementAtIndex(transCount);
    994.  
    995. skeletonParentNames.GetArrayElementAtIndex(transCount).stringValue = skeletonMap[t.name].parentName;
    996.  
    997.  
    998.  
    999. skeletonPositions.InsertArrayElementAtIndex(transCount);
    1000.  
    1001. skeletonPositions.GetArrayElementAtIndex(transCount).vector3Value = t.localPosition;
    1002.  
    1003.  
    1004.  
    1005. Vector4 transTempRot = new Vector4(t.localRotation.x, t.localRotation.y, t.localRotation.z, t.localRotation.w);
    1006.  
    1007. skeletonRotations.InsertArrayElementAtIndex(transCount);
    1008.  
    1009. skeletonRotations.GetArrayElementAtIndex(transCount).vector4Value = transTempRot;
    1010.  
    1011.  
    1012.  
    1013. skeletonScales.InsertArrayElementAtIndex(transCount);
    1014.  
    1015. skeletonScales.GetArrayElementAtIndex(transCount).vector3Value = t.localScale;
    1016.  
    1017. transCount++;
    1018.  
    1019. }
    1020.  
    1021. }
    1022.  
    1023. armatureSO.ApplyModifiedProperties();
    1024.  
    1025. }
    1026.  
    1027. public static BoneHelper ReadAWOCArmature(GameObject targetGameObject, SerializedObject armatureSO)
    1028.  
    1029. {
    1030.  
    1031. SerializedProperty mboneNames = armatureSO.FindProperty("boneNames");
    1032.  
    1033.  
    1034.  
    1035. SerializedProperty skeletonNames = armatureSO.FindProperty("skeletonNames");
    1036.  
    1037. SerializedProperty skeletonParentNames = armatureSO.FindProperty("skeletonParentNames");
    1038.  
    1039.  
    1040.  
    1041. SerializedProperty skeletonPositions = armatureSO.FindProperty("skeletonPositions");
    1042.  
    1043. SerializedProperty skeletonRotations = armatureSO.FindProperty("skeletonRotations");
    1044.  
    1045. SerializedProperty skeletonScales = armatureSO.FindProperty("skeletonScales");
    1046.  
    1047.  
    1048.  
    1049. string rootBoneName = armatureSO.FindProperty("rootBoneName").stringValue;
    1050.  
    1051. int boneCount = armatureSO.FindProperty("bonesLength").intValue;
    1052.  
    1053. List<string> boneNames = new List<string>();
    1054.  
    1055.  
    1056.  
    1057. for (int i = 0; i < boneCount; i++)
    1058.  
    1059. {
    1060.  
    1061. boneNames.Add(mboneNames.GetArrayElementAtIndex(i).stringValue);
    1062.  
    1063. }
    1064.  
    1065.  
    1066.  
    1067. BoneHelper boneHelper = new BoneHelper();
    1068.  
    1069. boneHelper.bones = new Transform[boneCount];
    1070.  
    1071.  
    1072.  
    1073. Dictionary<string, SkeletonGO> skeletonMap = new Dictionary<string, SkeletonGO>();
    1074.  
    1075.  
    1076.  
    1077. int skeletonLength = armatureSO.FindProperty("skeletonLength").intValue;
    1078.  
    1079.  
    1080.  
    1081. for (int i = 0; i < skeletonLength; i++)
    1082.  
    1083. {
    1084.  
    1085. GameObject go = new GameObject(skeletonNames.GetArrayElementAtIndex(i).stringValue);
    1086.  
    1087. go.transform.localPosition = skeletonPositions.GetArrayElementAtIndex(i).vector3Value;
    1088.  
    1089. Vector4 tempGORot = skeletonRotations.GetArrayElementAtIndex(i).vector4Value;
    1090.  
    1091. go.transform.localRotation = new Quaternion(tempGORot.x, tempGORot.y, tempGORot.z, tempGORot.w);
    1092.  
    1093. go.transform.localScale = skeletonScales.GetArrayElementAtIndex(i).vector3Value;
    1094.  
    1095.  
    1096.  
    1097.  
    1098.  
    1099. string parentName = skeletonParentNames.GetArrayElementAtIndex(i).stringValue;
    1100.  
    1101.  
    1102.  
    1103. skeletonMap.Add(go.name, new SkeletonGO(go.name, parentName, go.transform));
    1104.  
    1105.  
    1106.  
    1107. go.transform.SetParent(parentName == string.Empty ? targetGameObject.transform : skeletonMap[parentName].transform, false);
    1108.  
    1109.  
    1110.  
    1111. int boneIndex = boneNames.FindIndex(x => x == go.name);
    1112.  
    1113.  
    1114.  
    1115. if (boneIndex >= 0)
    1116.  
    1117. {
    1118.  
    1119. boneHelper.bones[boneIndex] = go.transform;
    1120.  
    1121.  
    1122.  
    1123. // Assign root bone
    1124.  
    1125. if (go.name == rootBoneName)
    1126.  
    1127. {
    1128.  
    1129. boneHelper.rootBoneTransform = go.transform;
    1130.  
    1131. }
    1132.  
    1133. }
    1134.  
    1135. }
    1136.  
    1137. return boneHelper;
    1138.  
    1139. }
    1140.  
    1141. }
    1142.  

    I really hope someone can suggest something that might help. Thanks in advance for your consideration.
     
  2. Adrian

    Adrian

    Joined:
    Apr 5, 2008
    Posts:
    1,051
    What exactly are you trying to achieve?

    Serializing it manually only really makes sense when you want to do it at runtime. If you're in the editor, why not saving the renderer / bones as a prefab (and the mesh as a mesh object, if necessary)? Then you can just make them addressable as you do with any other asset.
     
  3. standstilldigitalmedia

    standstilldigitalmedia

    Joined:
    Dec 26, 2021
    Posts:
    6
    I just want to separate the armature and meshes into different objects so I can reassemble them in whatever configuration I want at run time. I have a character with several suits of armor, each of which is comprised of several different meshes. I want to only load the meshes that the character has equipped.

    The way you described is the obvious way to go about it. I spent several weeks trying to figure out creating prefabs from a script but I just kept running into dead ends. Most of the info I find on Google is obsolete.

    Code (csharp):
    1.  
    2. foreach (Transform child in wph.originalAvatar.transform)
    3.                     {
    4.                         SkinnedMeshRenderer targetSMR = child.GetComponent<SkinnedMeshRenderer>();
    5.                         if (targetSMR)
    6.                         {
    7.                             GameObject tempGO = new GameObject(targetSMR.name);
    8.                             tempGO = child.gameObject;
    9.                             PrefabUtility.SaveAsPrefabAsset(child.gameObject, "Assets/Resources/" + targetSMR.name + ".prefab");
    10.                         }
    11.                     }
    12.  
    If I drag a prefab from the projects folder, drag the original file, or drag an object from the Hierarchy into an EditorGUILayout.ObjectField, I get ArgumentException: Can't save persistent object as a prefab asset. If I remove the + ".prefab" from the file name, same.
     
  4. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    6,003
    Then you'd be better of exporting each piece separately.
     
  5. standstilldigitalmedia

    standstilldigitalmedia

    Joined:
    Dec 26, 2021
    Posts:
    6
    With UMA no longer being maintained, I had in mind to create something similar. When UMA first began, Unity was a completely different animal than it is today. Uma barely scrapes along now and it won't be long before it is too out of date to be useful. I would like to give something back to the community that has supported me over the years with YouTube videos, forum posts, and free assets available on the asset store.

    I've done much of the ground work. I taught myself how to write shaders, work with addressables, editor scripting, etc. But I keep stumbling when it comes to saving meshes as separate objects. The serialization method in the link in my original post works great for reading and writing a mesh and armature from one game object to another. But when I save the new game object down as a prefab and load it back up, the mesh has disappeared somehow.

    Code (csharp):
    1.  
    2. byte[] rootSkeletonBytes = VGS.Common.VGS_SkinnedMeshUtility.Serialize(smr, rootSkeletonTransform, true, true, true);
    3. GameObject tempGo = new GameObject();
    4. GameObject tempChild = new GameObject();
    5. tempChild.transform.parent = tempGo.transform;
    6. tempChild.AddComponent<SkinnedMeshRenderer>();
    7. VGS.Common.VGS_SkinnedMeshUtility.Deserialize(tempGo, tempChild.GetComponent<SkinnedMeshRenderer>(), rootSkeletonBytes, true);
    8. PrefabUtility.SaveAsPrefabAsset(tempGo, "Assets/Resources/" + smr.name + ".prefab");
    9.  
    tempGO is successfully created and has a nice pink mesh as you would expect with no material set. The asset saved to disk has the skeletal structure and even a SkinnedMeshRenderer on the child game object but the mesh is missing from the SkinnedMeshRenderer.
     
  6. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    6,003
    I haven't looked to deeply at your code, but meshes are their own assets. You need to save the mesh as it's own asset as well, then hook up a skinned mesh renderer to said mesh asset.
     
  7. standstilldigitalmedia

    standstilldigitalmedia

    Joined:
    Dec 26, 2021
    Posts:
    6
    That was the final piece of the puzzle. Thank you so very much. I think I can take it from here.
     
    spiney199 likes this.
  8. hopeful

    hopeful

    Joined:
    Nov 20, 2013
    Posts:
    5,632
    UMA is no longer maintained? The Discord is still active. UMA 2.13 alpha was released in December.

    Did I miss some sort of notification ...?
     
  9. standstilldigitalmedia

    standstilldigitalmedia

    Joined:
    Dec 26, 2021
    Posts:
    6
    I'm not aware of their Discord. I can't seem to find it by Googling "unity uma discord." Looking at the asset store and the GitHub repo, it appears to be a little out of date.

    On the asset store, the latest release date was Apr 18, 2021.
    https://assetstore.unity.com/packages/3d/characters/uma-2-unity-multipurpose-avatar-35611

    The last commit on GitHub was 2 years ago
    https://github.com/umasteeringgroup/UMA

    Edit: I found this. I wasn't aware it was still going. That changes everything.
    https://github.com/umasteeringgroup/UMA/releases
     
    hopeful likes this.
  10. hopeful

    hopeful

    Joined:
    Nov 20, 2013
    Posts:
    5,632
    If you still don't have the Discord link, I believe it's in the documentation.
     
  11. standstilldigitalmedia

    standstilldigitalmedia

    Joined:
    Dec 26, 2021
    Posts:
    6
    To be honest, I'm not interested in using UMA as an asset but more as an inspiration for an asset I am working on. I had plans to monetize it with a free version and a pro version. I guess the only thing that really changes for me is the monetization aspect. I can't and don't want to compete with UMA. AWOC (Avatar Wardrobe Organizer and Colorer) will just be an educational tool now.