Search Unity

Showcase How to write world position to SkinnedMeshRenderer under pose

Discussion in 'Scripting' started by WARdd, Jan 24, 2023.

  1. WARdd

    WARdd

    Joined:
    Aug 10, 2015
    Posts:
    29
    This was a rather tricky issue I couldn't find much help for, so I didn't want to let the solution go without some coverage:
    I wanted to edit the mesh under a SkinnedMeshRenderer, in order to move its vertices to a determined world position, but do this while the bones are in a non-default pose. To do this you need the inverse of a bone transformation, which requires you to add and multiply matrices together, annoyingly Unity's builtin Matrix4x4 doesn't have methods for this, so a little hack included.

    Code (CSharp):
    1.  
    2. public SkinnedMeshRenderer smr;
    3.  
    4. private void SetMeshAtCurrentPose() {
    5.  
    6.         Transform[] bones = smr.bones;
    7.  
    8.         int vertexCount = smr.sharedMesh.vertexCount;
    9.         Vector3[] vertices = new Vector3[vertexCount];
    10.  
    11.         Matrix4x4[] bt = new Matrix4x4[bones.Length];
    12.         Matrix4x4[] bindPoses = smr.sharedMesh.bindposes;
    13.         for (int i = 0; i < bones.Length; i++)
    14.             bt[i] = bones[i].localToWorldMatrix * bindPoses[i];
    15.  
    16.         BoneWeight[] bws = smr.sharedMesh.boneWeights;
    17.  
    18.         for (int i = 0; i < vertexCount; i++) {
    19.             BoneWeight bw = bws[i];
    20.             Vector4[] vecForm = new Vector4[4];
    21.             LinComb(vecForm, bt[bw.boneIndex0], bw.weight0);
    22.             LinComb(vecForm, bt[bw.boneIndex1], bw.weight1);
    23.             LinComb(vecForm, bt[bw.boneIndex2], bw.weight2);
    24.             LinComb(vecForm, bt[bw.boneIndex3], bw.weight3);
    25.             Matrix4x4 ibt = new Matrix4x4(vecForm[0], vecForm[1], vecForm[2], vecForm[3]).inverse;
    26.  
    27.             Vector3 v = //Desired world position here
    28.             vertices[i] = ibt.MultiplyPoint3x4(v);
    29.         }
    30.  
    31.         Mesh posedMesh = Instantiate(smr.sharedMesh);
    32.         posedMesh.vertices = vertices;
    33.         smr.sharedMesh = posedMesh;
    34.     }
    35.  
    36.     private static void LinComb(Vector4[] lc, Matrix4x4 mat, float scalar) {
    37.         for (int i = 0; i < 4; i++)
    38.             lc[i] += scalar * mat.GetColumn(i);
    39.     }
    40.  
     
    Last edited: Jan 24, 2023
    halley likes this.