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

Animation rotates object 90 degrees

Discussion in 'Animation' started by ComboRoutine, Nov 14, 2015.

  1. ComboRoutine

    ComboRoutine

    Joined:
    Feb 3, 2014
    Posts:
    24


    See video for clarification.

    Not sure what further info to provide as I'm still not entirely sure where the issue stems from. Mesh/skeleton made in Blender, animation type's humanoid, using a mesh/skeleton combiner script (kindly provided by Trigve, along with assistance on implementation and other issues).
    Tringve's combiner script:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections.Generic;
    4.  
    5. using HierarchyDict = System.Collections.Generic.Dictionary<string, UnityEngine.Transform>;
    6. using BoneTransformDict = System.Collections.Generic.Dictionary<string, utils.Tuple<UnityEngine.Transform, string>>;
    7.  
    8. namespace utils
    9. {
    10.     public class MeshCombiner
    11.     {
    12. #region Operations
    13.         //! Combine mesh.
    14.         /*!
    15.             \return combined mesh instance.
    16.         */
    17.         public static GameObject Combine(List<SkinnedMeshRenderer> SkinnedRenderers)
    18.         {
    19.             // Generated GO
    20.             GameObject final_mesh_go = new GameObject("Mesh");
    21.             // Dummy parent holder
    22.             GameObject dummy_parent = new GameObject("DummyParent");
    23.  
    24.             // All available bones
    25.             var all_bones = new BoneTransformDict();
    26.             // Traverse through all skinned mesh renderers
    27.             foreach(var renderer in SkinnedRenderers)
    28.             {
    29.                 var renderer_bones = renderer.bones;
    30.                 foreach (var bone in renderer_bones)
    31.                 {
    32.                     // Bone doesn't exist, add it
    33.                     if (!all_bones.ContainsKey(bone.name))
    34.                         all_bones[bone.name] = new utils.Tuple<Transform, string>(bone, bone.parent.name);
    35.                 }
    36.             }
    37.  
    38.             var combineInstanceArrays = new Dictionary<Material, List<CombineInstance>>();
    39.             var bone_weights = new Dictionary<Mesh, BoneWeight[]>();
    40.             // Map between bone name and index
    41.             var added_bones = new Dictionary<string, int>();
    42.             // List of child objects holding the skinned mesh renderers to be
    43.             // destroyed when finished
    44.             var child_objects_to_destroy = new List<GameObject>();
    45.  
    46.             int bone_index = 0;
    47.             foreach(var renderer in SkinnedRenderers)
    48.             {
    49.                 child_objects_to_destroy.Add(renderer.transform.parent.gameObject);
    50.  
    51.                 var renderer_bones = renderer.bones;
    52.                 // Add all bones as first and save the indices of them
    53.                 foreach (var bone in renderer_bones)
    54.                 {
    55.                     // Bone not yet added
    56.                     if (!added_bones.ContainsKey(bone.name))
    57.                         added_bones[bone.name] = bone_index++;
    58.                 }
    59.                 // Adjust bone weights indices based on real indices of bones
    60.                 var bone_weights_list = new BoneWeight[renderer.sharedMesh.boneWeights.Length];
    61.                 var renderer_bone_weights = renderer.sharedMesh.boneWeights;
    62.                 for (int i = 0; i < renderer_bone_weights.Length; ++i)
    63.                 {
    64.  
    65.                     BoneWeight current_bone_weight = renderer_bone_weights[i];
    66.  
    67.                     current_bone_weight.boneIndex0 = added_bones[renderer_bones[current_bone_weight.boneIndex0].name];
    68.                     current_bone_weight.boneIndex2 = added_bones[renderer_bones[current_bone_weight.boneIndex2].name];
    69.                     current_bone_weight.boneIndex3 = added_bones[renderer_bones[current_bone_weight.boneIndex3].name];
    70.                     current_bone_weight.boneIndex1 = added_bones[renderer_bones[current_bone_weight.boneIndex1].name];
    71.  
    72.                     bone_weights_list[i] = current_bone_weight;
    73.                 }
    74.                 bone_weights[renderer.sharedMesh] = bone_weights_list;
    75.  
    76.                 // Handle bad input
    77.                 if (renderer.sharedMaterials.Length != renderer.sharedMesh.subMeshCount)
    78.                 {
    79.                     Debug.LogError("Mismatch between material count and submesh count. Is this the correct MeshRenderer?");
    80.                     continue;
    81.                 }
    82.  
    83.                 // Prepare stuff for mesh combination with same materials
    84.                 for (int i = 0; i < renderer.sharedMesh.subMeshCount; i++)
    85.                 {
    86.                     // Material not in dict, add it
    87.                     if (!combineInstanceArrays.ContainsKey(renderer.sharedMaterials[i]))
    88.                         combineInstanceArrays[renderer.sharedMaterials[i]] = new List<CombineInstance>();
    89.                     var actual_mat_list = combineInstanceArrays[renderer.sharedMaterials[i]];
    90.                     // Add new instance
    91.                     var combine_instance = new CombineInstance();
    92.                     combine_instance.transform = renderer.transform.localToWorldMatrix;
    93.                     combine_instance.subMeshIndex = i;
    94.                     combine_instance.mesh = renderer.sharedMesh;
    95.  
    96.                     actual_mat_list.Add(combine_instance);
    97.                 }
    98.                 // No need to use it anymore
    99.                 renderer.enabled = false;
    100.             }
    101.             var bones_hierarchy = new HierarchyDict();
    102.             // Recreate bone structure
    103.             foreach (var bone in all_bones)
    104.             {
    105.                 // Bone not processed, process it
    106.                 if (!bones_hierarchy.ContainsKey(bone.Key))
    107.                     AddParent(bone.Key, bones_hierarchy, all_bones, dummy_parent);
    108.             }
    109.  
    110.             // Create bone array from preprocessed dict
    111.             var bones = new Transform[added_bones.Count];
    112.             foreach (var bone in added_bones)
    113.                 bones[bone.Value] = bones_hierarchy[bone.Key];
    114.  
    115.             // Get the root bone
    116.             Transform root_bone = bones[0];
    117.  
    118.             while (root_bone.parent != null)
    119.             {
    120.                 // Get parent
    121.                 if (bones_hierarchy.ContainsKey(root_bone.parent.name))
    122.                     root_bone = root_bone.parent;
    123.                 else
    124.                     break;
    125.             }
    126.  
    127.  
    128.             // Create skinned mesh renderer GO
    129.             GameObject combined_mesh_go = new GameObject("Combined");
    130.             combined_mesh_go.transform.parent = final_mesh_go.transform;
    131.             combined_mesh_go.transform.localPosition = Vector3.zero;
    132.  
    133.             // Fill bind poses
    134.             var bind_poses = new Matrix4x4[bones.Length];
    135.             for (int i = 0; i < bones.Length; ++i)
    136.                 bind_poses[i] = bones[i].worldToLocalMatrix * combined_mesh_go.transform.localToWorldMatrix;
    137.  
    138.             // Need to move it to new GO
    139.             root_bone.parent = final_mesh_go.transform;
    140.  
    141.             // Combine meshes into one
    142.             var combined_new_mesh = new Mesh();
    143.             var combined_vertices = new List<Vector3>();
    144.             var combined_uvs = new List<Vector2>();
    145.             var combined_indices = new List<int[]>();
    146.             var combined_bone_weights = new List<BoneWeight>();
    147.             var combined_materials = new Material[combineInstanceArrays.Count];
    148.  
    149.             var vertex_offset_map = new Dictionary<Mesh, int>();
    150.  
    151.             int vertex_index_offset = 0;
    152.             int current_material_index = 0;
    153.  
    154.             foreach (var combine_instance in combineInstanceArrays)
    155.             {
    156.                 combined_materials[current_material_index++] = combine_instance.Key;
    157.                 var submesh_indices = new List<int>();
    158.                 // Process meshes for each material
    159.                 foreach (var combine in combine_instance.Value)
    160.                 {
    161.                     // Update vertex offset for current mesh
    162.                     if (!vertex_offset_map.ContainsKey(combine.mesh))
    163.                     {
    164.                         // Add vertices for mesh
    165.                         combined_vertices.AddRange(combine.mesh.vertices);
    166.                         // Set uvs
    167.                         combined_uvs.AddRange(combine.mesh.uv);
    168.                         // Add weights
    169.                         combined_bone_weights.AddRange(bone_weights[combine.mesh]);
    170.  
    171.                         vertex_offset_map[combine.mesh] = vertex_index_offset;
    172.                         vertex_index_offset += combine.mesh.vertexCount;
    173.                     }
    174.                     int vertex_current_offset = vertex_offset_map[combine.mesh];
    175.  
    176.                     var indices = combine.mesh.GetTriangles(combine.subMeshIndex);
    177.                     // Need to "shift" indices
    178.                     for (int k = 0; k < indices.Length; ++k)
    179.                         indices[k] += vertex_current_offset;
    180.  
    181.                     submesh_indices.AddRange(indices);
    182.                 }
    183.                 // Push indices for given submesh
    184.                 combined_indices.Add(submesh_indices.ToArray());
    185.             }
    186.  
    187.             combined_new_mesh.vertices = combined_vertices.ToArray();
    188.             combined_new_mesh.uv = combined_uvs.ToArray();
    189.             combined_new_mesh.boneWeights = combined_bone_weights.ToArray();
    190.  
    191.             combined_new_mesh.subMeshCount = combined_materials.Length;
    192.             for (int i = 0; i < combined_indices.Count; ++i)
    193.                 combined_new_mesh.SetTriangles(combined_indices[i], i);
    194.  
    195.             // Create mesh renderer
    196.             SkinnedMeshRenderer combined_skin_mesh_renderer = combined_mesh_go.AddComponent<SkinnedMeshRenderer>();
    197.             combined_skin_mesh_renderer.sharedMesh = combined_new_mesh;
    198.             combined_skin_mesh_renderer.bones = bones;
    199.             combined_skin_mesh_renderer.rootBone = root_bone;
    200.             combined_skin_mesh_renderer.sharedMesh.bindposes = bind_poses;
    201.  
    202.             combined_skin_mesh_renderer.sharedMesh.RecalculateNormals();
    203.             combined_skin_mesh_renderer.sharedMesh.RecalculateBounds();
    204.             combined_skin_mesh_renderer.sharedMaterials = combined_materials;
    205.  
    206.             // Destroy children
    207.             foreach (var child in child_objects_to_destroy)
    208.                 GameObject.DestroyImmediate(child);
    209.             // Destroy dummy parent
    210.             GameObject.DestroyImmediate(dummy_parent);
    211.  
    212.             return final_mesh_go;
    213.         }
    214.  
    215.         static void AddParent(string BoneName, HierarchyDict BoneHierarchy, BoneTransformDict AllBones, GameObject DummyParent)
    216.         {
    217.             Transform actual_bone = null;
    218.             // Must be bone
    219.             if (AllBones.ContainsKey(BoneName))
    220.             {
    221.                 var bone_tuple = AllBones[BoneName];
    222.                 // Add parent recursively if not added
    223.                 if (!BoneHierarchy.ContainsKey(bone_tuple._2))
    224.                 {
    225.                     AddParent(bone_tuple._2, BoneHierarchy, AllBones, DummyParent);
    226.                     // Unparent all children of parents
    227.                     Unparent(BoneHierarchy[bone_tuple._2], DummyParent);
    228.                 }
    229.  
    230.  
    231.                 bone_tuple._1.parent = BoneHierarchy[bone_tuple._2];
    232.                 actual_bone = bone_tuple._1;
    233.             }
    234.  
    235.             BoneHierarchy[BoneName] = actual_bone;
    236.         }
    237.  
    238.         static void Unparent(Transform Parent, GameObject DummyParent)
    239.         {
    240.             if (Parent != null)
    241.             {
    242.                 var unparent_list = new List<Transform>();
    243.  
    244.                 foreach (Transform child in Parent.transform)
    245.                     unparent_list.Add(child);
    246.  
    247.                 foreach (var child in unparent_list)
    248.                     child.parent = DummyParent.transform;
    249.             }
    250.         }
    251. #endregion
    252.     }
    253. }
    254.  

    Using the rotation fix in Blender to fix the whole Z-Y-mess, which basically just involves giving the mesh a 90 degree rotation, so that seems like a likely culprit, but it's also very much necessary, and the rotation only appears when playing an animation, so hell if I know.

    Any thoughts? Ideas? Will provide details as needed. Would rather not upload the entire project as it's just a massive sandbox of different techniques and unused assets everywhere, but if someone thinks it's required, it can be done
     
  2. medhue

    medhue

    Joined:
    Aug 24, 2014
    Posts:
    176
    What z y mess? When I create something in Blender for Unity, I do nothing special with z or y. I make sure my meshes don't have any rotation or scaling, as well as the amature, and I export everything with FBX assci, and default z and y.
     
  3. ComboRoutine

    ComboRoutine

    Joined:
    Feb 3, 2014
    Posts:
    24
    .blend files face the Y in a different direction than Unity does. .fbx files don't have that issue as far as I know, but those present me with a whole new set of issues. Meshes turn into amorphous messes instead of obeying the skeleton, and some parts refuse to get combined at all (note: the files that don't get combined always have Culling Mode on "Always Animate", as opposed to "Cull Update Transforms" on the parts that work. No idea what that's about).
    Scale's also 1/100 of normal, and changing the export options to scale 100 doesn't seem to change anything.

    I figured the rotation sounds easier to figure out, so that's what I presented to the forums
     
    Last edited: Nov 15, 2015
  4. medhue

    medhue

    Joined:
    Aug 24, 2014
    Posts:
    176
    What could be going on is that your vertices are using more than 4 bones for weighting. Unity will only recognize 4 bone weights per vertice.
     
  5. ComboRoutine

    ComboRoutine

    Joined:
    Feb 3, 2014
    Posts:
    24
    I'm using pretty simple temporary meshes, and the weights are fairly straight cuts. Went through some of the meshes and couldn't find any vertices taking weights from more than three bones, so that doesn't seem likely. Then again, there's always the chance that something goes down during the combining process, but I'unno. Thanks though