Hello,
I noticed a strange behavior of skinned mesh vertices after updating mesh weights. The visible position of the vertex is not the same as the one presented in the baked mesh (and displayed in OnDrawGizmos() ).
Mesh resultMesh = Instantiate(bodySkin.sharedMesh);
BoneWeight[] weights = resultMesh.boneWeights;
Mesh meshBeforeWeightsUpdate = new Mesh();
bodySkin.BakeMesh(meshBeforeWeightsUpdate);
print(meshBeforeWeightsUpdate.vertices[vertIndex].magnitude); // 1.301173
weights[vertIndex].weight0 = 0.627f; // set some test weight value
resultMesh.boneWeights = weights;
bodySkin.sharedMesh = resultMesh;
Mesh meshAfterWeightsUpdate = new Mesh();
bodySkin.BakeMesh(meshAfterWeightsUpdate);
print(meshAfterWeightsUpdate.vertices[vertIndex].magnitude); // 1.313242 - the position is changed, but not as predicted
debugVerts.Add(bodySkin.transform.TransformPoint(meshAfterWeightsUpdate.vertices[vertIndex]));
How can I get the real position of the given vertex, after the corresponding weights update, for the further calculations and for proper displaying in Gizmos?
Is this real vertex position calculated in GPU and is not accessible in code?
I turned off GPU Skinning in Project Settings > Player, but the result is the same.
Thank you.
Using Matrix / boneWeights calculations instead of baking the mesh, didn’t help.
The visible vertex is still not the same as the calculated one.
using System.Collections;
using UnityEngine;
public class SkinnedVertices : MonoBehaviour
{
public SkinnedMeshRenderer skin;
private const int vertIndex = 7359;
void Start()
{
StartCoroutine(UpdateWeights());
}
private IEnumerator UpdateWeights()
{
Mesh resultMesh = Instantiate(skin.sharedMesh);
BoneWeight[] weights = resultMesh.boneWeights;
while(true)
{
weights[vertIndex].weight0 = 0.6f + Random.Range(0f, 0.06f);
resultMesh.boneWeights = weights;
skin.sharedMesh = resultMesh;
yield return new WaitForSeconds(1.5f);
}
}
private void OnDrawGizmos()
{
if (Application.isPlaying)
{
Matrix4x4[] boneMatrices = new Matrix4x4[skin.bones.Length];
for (int i = 0; i < boneMatrices.Length; i++)
boneMatrices[i] = skin.bones[i].localToWorldMatrix * skin.sharedMesh.bindposes[i];
BoneWeight weight = skin.sharedMesh.boneWeights[vertIndex];
Matrix4x4 bm0 = boneMatrices[weight.boneIndex0];
Matrix4x4 bm1 = boneMatrices[weight.boneIndex1];
Matrix4x4 bm2 = boneMatrices[weight.boneIndex2];
Matrix4x4 bm3 = boneMatrices[weight.boneIndex3];
Matrix4x4 vertexMatrix = new Matrix4x4();
for (int n = 0; n < 16; n++)
{
vertexMatrix[n] =
bm0[n] * weight.weight0 +
bm1[n] * weight.weight1 +
bm2[n] * weight.weight2 +
bm3[n] * weight.weight3;
}
Vector3 position = vertexMatrix.MultiplyPoint3x4(skin.sharedMesh.vertices[vertIndex]);
Gizmos.color = Color.red;
Gizmos.DrawSphere(position, 0.0005f);
}
}
}
In the following script we generate a new mesh based on the given SkinnedMesh, after some vertex weight is changed.
BakeMesh() does produce wrong geometry:
using UnityEngine;
public class SkinnedVertices : MonoBehaviour
{
public SkinnedMeshRenderer skin;
public MeshFilter testRenderer;
private Mesh testMesh;
private const int vertIndex = 7359;
private void Start()
{
testMesh = testRenderer.mesh;
skin.updateWhenOffscreen = true;
Mesh resultMesh = Instantiate(skin.sharedMesh);
BoneWeight[] weights = resultMesh.boneWeights;
weights[vertIndex].weight0 = 0.62f;
resultMesh.boneWeights = weights;
skin.sharedMesh = resultMesh;
skin.BakeMesh(testMesh);
}
}