Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Rotate object based on its relative position to a skinned mesh's bone

Discussion in 'Scripting' started by Ibizanhound, Feb 14, 2021.

  1. Ibizanhound

    Ibizanhound

    Joined:
    Jun 20, 2014
    Posts:
    18
    I have downloaded the "Mesh and object deformer" asset (free - https://assetstore.unity.com/packages/tools/modeling/mesh-and-object-deformers-for-unity-3d-81427) which has the ability to deform skinned meshes.

    Well... kind of...

    In reality it keeps a snapshot of the .sharedMesh of the skinned object as an array of vertices and checks on Update how close the object that you assign to be the deforming effector is to those Tpose vertices, then finds their equivelant vertices on the skinned mesh since they have the same indices and deforms them.

    Problem is the sharedMesh is in the initial pre-animated Tpose.
    So the moment the character's arms or torso deviate significantly from the Tpose, the effect can't work.

    Here is what I am trying to achieve:
    I am trying to rotate the relative position of the deforming effector to a bone
    (the upperArm bone at the moment as a proof of concept),
    back to the initial rotation that the bone had in the Tpose.

    My problem is that I 've been looking into rotation for 6 days now but I am not sure what is the right way to do this.
    As you can see in the code,
    I have rotated manually the object around the Z axis by 90 degrees,
    but in reality I have to calculate the angular difference between posed bone and the tposed one and use that to rotate the twin sphere (effector) back to the Tpose.
    Notice also that in the gif I 've attached, the twin ball (red) is falsely staying close to the red arm at all times and doesnt seem to take into account the swaying of the blue arm in relation to the blue ball.

    Any help would be appreciated!

    P.S. Links I ve looked into so far:
    https://answers.unity.com/questions/532297/rotate-a-vector-around-a-certain-point.html
    https://docs.unity3d.com/ScriptReference/Transform.RotateAround.html
    https://forum.unity.com/threads/rotating-a-vector-around-a-certain-point.198553/
    https://forum.unity.com/threads/rotating-a-vector-by-an-eular-angle.18485/

    Code (CSharp):
    1. void Start()
    2.     {
    3.  
    4.             InitialTPoseMesh = GetComponent<SkinnedMeshRenderer>().sharedMesh;
    5.  
    6.             GameObject characterObj = GameObject.Find("SkinnedMeshBody");
    7.  
    8.             characterBones = characterObj.GetComponentInChildren<SkinnedMeshRenderer>().bones;
    9.  
    10.             initialBonePosition = characterBones[16].position;//<--the bone with index value 16 corresponds to the upperArm in this specific rig;
    11.  
    12.             sphereOriginal = GameObject.Find("Sphere"); //Blue sphere
    13.  
    14.             sphereTwin = GameObject.Find("Sphere_Twin"); // Red sphere
    15.  
    16.  
    17.         }
    18.     }
    19.  
    20.  
    21.     void FixedUpdate()
    22.     {
    23.         CurrentRelativePosition = sphereOriginal.transform.position - characterBones[16].position; // Current relative position of Blue sphere to upperArm bone
    24.  
    25.         sphereTwin.transform.position = (Quaternion.Euler(0, 0, 90) * CurrentRelativePosition) + initialBonePosition;   //<--(Quaternion.Euler(0, 0, 90) this is what I am trying to find. How to replace the 0,0,90 with the angles of rotation between the current upperArm rotation and its initial Tpose rotation angle.
    26.     }
     

    Attached Files:

    • 2.gif
      2.gif
      File size:
      4.4 MB
      Views:
      318
  2. Ibizanhound

    Ibizanhound

    Joined:
    Jun 20, 2014
    Posts:
    18
    I just found out about Quaternion.FromToRotation and it seems to be what I needed (it works for the upperArm bone "proof of concept" test I am currently doing at least!)
    It is as easy as feeding it with the current directional vector you have and where you wanted to be directed to, and once you have that as a "var" you can multiply it to any vector3 and rotate it with those quaternion angles!
    Here is the code I used. Feel free to feedback!
    Code (CSharp):
    1.    
    2.  
    3. void Start()
    4. {
    5.            InitialTPoseMesh = GetComponent<SkinnedMeshRenderer>().sharedMesh;
    6.  
    7.            GameObject characterObj = GameObject.Find("SkinnedMeshBody");
    8.  
    9.            characterBones = characterObj.GetComponentInChildren<SkinnedMeshRenderer>().bones;
    10.  
    11.            initialBoneDirection = characterBones[17].position - characterBones[16].position;
    12. }
    13.  
    14. void FixedUpdate()
    15.     {
    16.        float dist = Vector3.Distance(characterBones[16].position, sphereOriginal.transform.position);
    17.  
    18.        currentBoneDirection = characterBones[17].position - characterBones[16].position;
    19.  
    20.        var reverseBoneRotation = Quaternion.FromToRotation(currentBoneDirection, initialBoneDirection);
    21.  
    22.        CurrentRelativePosition = sphereOriginal.transform.position - characterBones[16].position;
    23.  
    24.        CurrentRelativePosition = reverseBoneRotation * CurrentRelativePosition;
    25.  
    26.        sphereTwin.transform.position = characterBones[16].position;
    27.  
    28.        sphereTwin.transform.position = characterBones[16].position + CurrentRelativePosition.normalized * dist;
    29.  
    30.      }