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. Dismiss Notice

SkinnedMeshRenderer: Mesh has been changed to one which is not compatibile with the expected mesh da

Discussion in 'General Graphics' started by burningmime, Aug 17, 2021.

  1. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    Has anyone encountered this before? I am changing the mesh for an SMR in code using this property: https://docs.unity3d.com/ScriptReference/SkinnedMeshRenderer-sharedMesh.html

    I have 2 meshes with identical input settings. Both are skinned meshes with an avatar (and the exact same bone structure) Strangely if I set it to the other mesh on startup and then try to change it back, it'll error out with the first mesh. So it's not the mesh itself that's the problem; it's just the fact that I'm changing it.

    EDIT: title trucated it, here's the full log message:

    Code (CSharp):
    1. SkinnedMeshRenderer: Mesh has been changed to one which is not compatibile with the expected mesh data size and vertex stride. Aborting rendering.
    2. UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)
    3.  
     
    Last edited: Aug 25, 2021
  2. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    I did some more investigating and the vertex layout is IDENTICAL between the two:

    Code (CSharp):
    1.  
    2. Mesh ChibiChara_Base_Boy:
    3.     vertexCount: 2070
    4.     Attributes:
    5.         Position: Stream=0, Offset=0, Format=Float32, Dimension=3
    6.         Normal: Stream=0, Offset=12, Format=Float32, Dimension=3
    7.         Tangent: Stream=0, Offset=24, Format=Float32, Dimension=4
    8.         TexCoord0: Stream=1, Offset=0, Format=Float32, Dimension=2
    9.         BlendWeight: Stream=2, Offset=0, Format=Float32, Dimension=4
    10.         BlendIndices: Stream=2, Offset=16, Format=UInt32, Dimension=4
    11.     Streams:
    12.         GetVertexBufferStride(0): 40
    13.         GetVertexBufferStride(1): 8
    14.         GetVertexBufferStride(2): 32
    15.  
    16. Mesh ChibiChara_Base_Girl:
    17.     vertexCount: 2090
    18.     Attributes:
    19.         Position: Stream=0, Offset=0, Format=Float32, Dimension=3
    20.         Normal: Stream=0, Offset=12, Format=Float32, Dimension=3
    21.         Tangent: Stream=0, Offset=24, Format=Float32, Dimension=4
    22.         TexCoord0: Stream=1, Offset=0, Format=Float32, Dimension=2
    23.         BlendWeight: Stream=2, Offset=0, Format=Float32, Dimension=4
    24.         BlendIndices: Stream=2, Offset=16, Format=UInt32, Dimension=4
    25.     Streams:
    26.         GetVertexBufferStride(0): 40
    27.         GetVertexBufferStride(1): 8
    28.         GetVertexBufferStride(2): 32
    29.  
    I can try submitting a bug but in my experience it's a crapshoot whether anyone even reads the bug.
     
  3. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    Bump again; this is semi-blocking. I am also changing avatar if that's relevant. I have tried both ways (changing avatar first or changing mesh first) and both break.
     
  4. paddan

    paddan

    Unity Technologies

    Joined:
    Jun 19, 2019
    Posts:
    13
    Hello,
    I havn't looked into this extensively but my guess is that you need the same vertex count on both meshes.
     
    burningmime likes this.
  5. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    Thanks for getting back. So basically, you can't change the sharedMesh for a SkinnedMeshRenderer at runtime? Kinda sucks, but I guess it's not the end of the world if it needs recreated as a new GameObject.
     
    Kokowolo likes this.
  6. paddan

    paddan

    Unity Technologies

    Joined:
    Jun 19, 2019
    Posts:
    13
    I think technically you can change the mesh, but you need to be very mindful of the data. The Skinned Mesh Renderer does some internal book keeping and changing to a mesh during runtime can lead to incorrect rendering if the buffers don't match up. Since the vertex count is larger in the second mesh the buffers wont match, even though the layout is the same. In your case it is probably better to have two separate Game Objects and switch between them like you say.
    Hope this information helps
     
    burningmime likes this.
  7. Kokowolo

    Kokowolo

    Joined:
    Mar 26, 2020
    Posts:
    41
    I'm having this issue too, and I'm a bit confused as to what's going on behind the scenes. It seems that the error is independent of the vertex count/buffer size (at least in my case) and has everything to do with the really confusing, non-editor SkinnedMeshRenderer.bones property.

    Here I have this function to set the mesh of a SkinnedMeshRenderer:
    Code (CSharp):
    1. public void Set(ref SkinnedMeshRenderer renderer, SkinnedMeshRenderer value)
    2. {
    3.     renderer.sharedMesh = value.sharedMesh;
    4.     renderer.bounds = next.bounds;
    5.  
    6.     renderer.rootBone = skeleton.RecursiveFind(next.rootBone.name);
    7. }
    Where skeleton is the root skeleton Transform for the Animator and
    Code (CSharp):
    1. /// <summary>
    2. /// Recursively searches a Transform's hierarchy to find a child by name n and return it
    3. /// </summary>
    4. /// <returns>The found child transform; Null if child with matching name isn't found</returns>
    5. public static Transform RecursiveFind(this Transform transform, string n)
    6. {
    7.     foreach (Transform child in transform)
    8.     {
    9.         if (child.name == n) return child;
    10.         Transform grandchild = child.RecursiveFind(n);
    11.         if (grandchild) return grandchild;
    12.     }
    13.     return null;
    14. }
    To solve this error, I had to manually set the internal SkinnedMeshRenderer.bones property at the bottom of my Set function
    Code (CSharp):
    1. Transform[] bones = new Transform[value.bones.Length];
    2. for (int i = 0; i < value.bones.Length; i++)
    3. {
    4.     bones[i] = skeleton.RecursiveFind(value.bones[i].name);
    5. }
    6. renderer.bones = bones;
    This is definitely my inefficient, rough draft way of doing this, but hopefully this points someone in the right direction when they search this error.
     
  8. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,112
    I was having the same problem with a generated mesh (in Editor, not runtime).

    I have a tool which modifies (replaces) meshes on SkinnedMeshRenderers. I was making a new mesh and then I assigned it to the mesh renderer vai code (in Editor, not at runtime). All was working fine, the new mesh had the right bones and there was no problem, but the error was shown anyhow. It was annoying.

    MY SOLUTION:
    I solved it by assigning NULL to the sharedMesh of the renderer. Then immediately afterwards I am assigning the new mesh (all in one frame). Now the error is gone. Maybe this works at runtime too.
    I guess by assigning NULL to the mesh some internal caches (buffers) are reset and then rebuilt once the new mesh is assigned.

    UPDATE:
    Turns out assigning NULL is not needed. The new Model Asset simply was not imported "fully" into the AssetDatabase. A simple
    AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
    fixed it.

    Hope this helps :)
     
    Last edited: Sep 9, 2022
    burningmime likes this.
  9. paddan

    paddan

    Unity Technologies

    Joined:
    Jun 19, 2019
    Posts:
    13
    Just to add a bit of context regarding bones.
    Bone weights and indices are stored in the vertex streams, so this is also part of the vertex data, even though the bone transforms themselves are not.
    Some more information: https://docs.unity3d.com/Manual/AnatomyofaMesh.html#bone-weight
     
    _geo__ likes this.
  10. Cleverlie

    Cleverlie

    Joined:
    Dec 23, 2013
    Posts:
    219
    I want to bump up this thread, couldn't we have a SkinnedMeshRenderer.ReplaceMesh() method that takes a mesh and replaces the sharedMesh doing the proper internal changes in buffers and whatever needs to happen? I feel like this a regular use case that is yet in 2023 not covered by the API, how come we can't change the mesh of a skinned mesh renderer?

    really our only solution is having to instantiate a full prefab and go and replace every reference to the old renderer to the new one in our scripts and components? that seems like an overhead nightmare just for something that is essential if you want to do something basic like just customize an avatar or do a merge of skinned meshes to save drawcalls.
     
  11. arkano22

    arkano22

    Joined:
    Sep 20, 2012
    Posts:
    1,660
    There's no one-size-fits-all solution for this. You can't take a skinned mesh and replace it with some other arbitrary mesh while keeping the same bone structure, it simply doesn't make any sense.

    The reason lies at the core of linear blend skinning: meshes have a list of bone weights, that is, for each vertex they store a number of bone indices and how much the vertex is influenced by that bone's motion. Then, the renderer has a list of bone transforms. The bones indices stored in the mesh and the transforms in the renderer's "bones" array must match.

    If you replace the skinned mesh with a different one, you can end up with vertices referencing bones that don't exist in the skeleton, bone influences that do not match the vertex/bone layout (such as vertices influenced by very distant bones), etc. Any solutions to this are situational and depend on the results you're looking for.

    Yep, really. Customizing an avatar and merging skinned meshes are pretty far from basic use cases, you need good understanding about mesh skinning and how all data contained in the mesh and the skeleton fits together to make it all work.
     
  12. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    The reason appears to be more sinister than that. That data is stored in the vertices; if you change to one with the exact same skeleton layout, everything should work out just fine. And it would, if skinning was done in the vertex shader like many engines do.

    However, Unity allocates a separate vertex buffer behind the scenes to store post-transform vertex positions*. In 2021.x and beyond, you can directly access it via GetVertexBuffer. These buffers are what need to be reallocated. @_geo__ 's solution of setting the mesh to null and then to the new one seems to cause this reallocation to happen. Unity could automatically reallocate when you change the mesh to one with a different vertex count, but you'd need source code access to fix it.

    Really, though, the error is incredibly misleading, since it says:

    Code (csharp):
    1. Mesh has been changed to one which is not compatibile with the expected mesh data size and vertex stride.
    Which just isn't true.

    * It allocates 2 of them and swaps between them so it can handle motion vectors correctly; the previous frame's skin position is available in your vertex shader as TEXCOORD4 or by using GetPreviousVertexBuffer.
     
    ghostitos and arkano22 like this.
  13. arkano22

    arkano22

    Joined:
    Sep 20, 2012
    Posts:
    1,660
    Wow ok, that's more sinister indeed. If the new mesh shares the same skeleton (so the skin data stored in it makes sense in the context of that particular skeleton, meaning no weights referencing bones that don't exist), it should definitely work. :eek:
     
    ghostitos likes this.