Assigning bone weights and indices directly to a vertex buffer.

I've been attempting to set bone weights and indices by writing directly to a vertex buffer, but it doesn't seem to work.

Assuming 4 dimensions for both weights and indices with float32 and uint32 format respectively, it seems I can read and add values correctly. Reading the floats works fine, and the bone indices can be read by casting the respective uint32 as a byte. Starting with a working mesh, and adding a few extra vertices, the extra vertices read the correct values - the following prints the correct results for the newly added vertices.

byte* ptr = origVertexBufferStartPtr + idx * bytesPerVertex;

for (int j = 0; j < bonesDimension; j++)
{
    float weight = *((float*)ptr);
    Debug.Log("Weight: " + weight);
    ptr += bytesPerWeight;
}
for (int j = 0; j < bonesDimension; j++)
{
    byte lowerIndex = *((byte*)ptr);
    Debug.Log("Index: " + lowerIndex);
    ptr += bytesPerIndex;
}

Despite this however, the weights don't seem to work correctly, and the values I afterwards get using

var bones = mesh.boneWeights;

are not correct, sometimes being negative etc.

Is there a way to make this work, and make the new bone related vertex values useable?

you didn't show any code on how you're updating your weights

[quote=“mabulous”, post:2, topic: 870476]
you didn’t show any code on how you’re updating your weights
[/quote]

I make a new vertex buffer. For original vertices that I want to use, I just copy the corresponding memory in the original buffer - this works fine.
For new vertices, I update the weights similarly to the code above, writing the values directly to the vertex buffer. E.g. something like this for indices (slightly different behavior depending on vertex attributes)

case VertexAttributeFormat.UInt32:
    for (int j = 0; j < bonesDimension; j++)
    {
        // Get current new vertex buffer position
        byte* tmp = (byte*)newVertexBufferPtr;
        // Assign
        *tmp = boneIndex;
        // Update pointer
        newVertexBufferPtr += 4;
    }
    break;

I doubt there’s something wrong with this, because printing out the values gives correct results - it’s the same function for both new and old vertices in the buffer, so if there was something wrong, I would expect both would be wrong.

and are you sure that your target architecture is Little-Endian? And are you clearing the other bytes of the index to 0? Depending on how you allocate the memory for that new buffer, it may not be zeroed out.

[quote=“mabulous”, post:4, topic: 870476]
and are you sure that your target architecture is Little-Endian? And are you clearing the other bytes of the index to 0? Depending on how you allocate the memory for that new buffer, it may not be zeroed out.
[/quote]

I think that not clearing out the memory is a good point, thanks. I’m explicitly assigning the values to the proper byte positions of the indices, but the other 3 are skipped and are uninitialized, I’ll test if zeroing them helps.

I must admit I don’t really know how the bone indices in the buffer work - the whole thing is confusing, because:
1, Apparently up to 255 bones per vertex are somehow possible - given that a vertex stream has at most 4 dimensions *32bits per bone (using uint32), I don’t see how the bones could in any way be encoded in a vertex buffer. It makes me think that perhaps using vertex buffers as source of bone weights and indices might deprecated, and skinning is now always done differently - https://docs.unity3d.com/ScriptReference/Mesh.SetBoneWeights.html would also seem to suggest this.

2, I can’t find any documentation regarding how the bone indices are actually stored in the vertex buffer. I’ve fooled around and noticed that the uint32 format etc is really just used as a stride, and just reading the first byte works, but it’s hacky and I’m not even sure whether it’s sure to always work.

[quote=“mabulous”, post:4, topic: 870476]
and are you sure that your target architecture is Little-Endian? And are you clearing the other bytes of the index to 0? Depending on how you allocate the memory for that new buffer, it may not be zeroed out.
[/quote]

Thanks, clearing the values to zero for the indices fixed the issue.

1 Like