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

Question How to make an exact copy of a mesh with blendshapes?

Discussion in 'Scripting' started by Steven-1, Oct 25, 2022.

  1. Steven-1

    Steven-1

    Joined:
    Sep 11, 2010
    Posts:
    451
    The following doesn't work for some reason. Does anyone know why?

    Or, alternatively, if anyone knows how to use MeshFilter.mesh in an editor script (which does exactly what I want) without unity logging a fake error, that would be great too.

    Code (CSharp):
    1. private Mesh copyMesh(Mesh mesh)
    2.         {
    3.             var newMesh = new Mesh();
    4.             newMesh.vertices = mesh.vertices;
    5.             newMesh.triangles = mesh.triangles;
    6.             newMesh.normals = mesh.normals;
    7.             newMesh.tangents = mesh.tangents;
    8.             newMesh.bounds = mesh.bounds;
    9.             //newMesh.blendShapeCount = mesh.blendShapeCount;
    10.             newMesh.boneWeights = mesh.boneWeights;
    11.             newMesh.bindposes = mesh.bindposes;
    12.             newMesh.colors = mesh.colors;
    13.             newMesh.name = mesh.name;
    14.             newMesh.uv = mesh.uv;
    15.             newMesh.uv2 = mesh.uv2;
    16.             newMesh.uv3 = mesh.uv3;
    17.             newMesh.uv4 = mesh.uv4;
    18.             newMesh.uv5 = mesh.uv5;
    19.             newMesh.uv6 = mesh.uv6;
    20.             newMesh.uv7 = mesh.uv7;
    21.             newMesh.uv8 = mesh.uv8;
    22.             for (int i = 0; i < mesh.blendShapeCount; i++)
    23.             {
    24.                 int framecount = mesh.GetBlendShapeFrameCount(i);
    25.                 for (int j = 0; j < framecount; j++)
    26.                 {
    27.                     Vector3[] deltavertices = new Vector3[mesh.vertexCount];
    28.                     Vector3[] deltanormals = new Vector3[mesh.vertexCount];
    29.                     Vector3[] deltatangents = new Vector3[mesh.vertexCount];
    30.                     mesh.GetBlendShapeFrameVertices(i, j, deltavertices, deltanormals, deltatangents);
    31.                     newMesh.AddBlendShapeFrame(mesh.GetBlendShapeName(i), mesh.GetBlendShapeFrameWeight(i, j), deltavertices, deltanormals, deltatangents);
    32.                 }
    33.             }
    34.             return newMesh;
    35.         }
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    4,019
    Which is? And why is it "fake"?
     
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,713
    Saw your necro in the other thread, what do you mean by it's the same mesh? Are you sure?

    Code (csharp):
    1.     void Start()
    2.     {
    3.         var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
    4.         var m1 = cube.GetComponent<MeshFilter>().mesh;
    5.         var m2 = Instantiate<Mesh>(m1);
    6.         Debug.Log("m1 = " + m1.GetInstanceID());
    7.         Debug.Log("m2 = " + m2.GetInstanceID());
    8.     }
    At a minimum it has a different instance ID... I'll leave it to you to test blendshapes and other goodies.
     
  4. Steven-1

    Steven-1

    Joined:
    Sep 11, 2010
    Posts:
    451
    yeah sorry, didn't have much time but wanted to post this question anyway.
    When you use MeshFilter.mesh (which makes an identical copy of the mesh, which is exactly what I want) in an editor script, Unity logs a message saying this is dangerous because calling MeshFilter.mesh in the editor can cause a memory leak (because a new copy is being made). It's a fake error because nothing is actually wrong, someone at Unity just decided to log this as an error rather than a warning. You can do Debug.LogError() yourself in a script, that doesn't make it a real error.
    The reason why it's not an error (or even an issue in the slightest) in my case is because I actually want the side effect of MeshFilter.mesh creating a copy. (The presumed intended use of MeshFilter.mesh is to simply make the meshfilter have a unique instance of the mesh at runtime so any modifications to it remain on the copy instead of the original)

    If they don't want us to use MeshFilter.mesh to copy meshes, I don't get why they dont provide a duplicate() or copy() function, since copying any kind of mesh isn't trivial.
     
  5. Steven-1

    Steven-1

    Joined:
    Sep 11, 2010
    Posts:
    451
    This code is actually different than the code in the other thread. In the other thread they use MeshFilter.sharedmesh.
    Like I said, MeshFilter.mesh does exactly what I want, except it logs an error in the console when used in the editor, and i don't want that as I'm working on an asset for on the asset store.
     
  6. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,315
    Perhaps this script will work for your purposes? Meant to be used to serialize meshes to binary, but you can merge the ser/des routines to copy a mesh directly instead: https://pastebin.com/yW91qEQh
     
    Steven-1 likes this.
  7. Steven-1

    Steven-1

    Joined:
    Sep 11, 2010
    Posts:
    451
    Thanks, I'll check it out
     
  8. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    4,019
    The MeshFilter doc actually has an example on how to make a copy of a mesh:
    Code (CSharp):
    1. Mesh mesh = GetComponent<MeshFilter>().sharedMesh;
    2. Mesh meshCopy = Instantiate(mesh);
    The reason why accessing the .mesh property raises a warning (or error) has more to do with the side effects. Once you access .mesh the sharedMesh is no longer the mesh that was previously assigned to the MeshFilter, it's the copy the first access to .mesh creates. This means any change to the original mesh will no longer be reflected on the object, at least not until the scene is reloaded or some other cases. This is at least an annoyance for artists working in that scene because they no longer see their changes being applied in a consistent manner, and end up cursing Unity. ;)

    Likewise, any change to the .mesh from scripts will not be persisted. Next time the scene is loaded, the original sharedMesh will be used (*), not any of the modifications done to the .mesh version. And that copy of the mesh? Possibly still in memory with no way of unloading it, unless restarting the editor! More cursing at Unity occuring at this point.

    *) Unless you also save that .mesh copy as an asset file and subsequently assign it to the MeshFilter's sharedMesh property and save the scene/prefab with that object which is making things even more complicated, and possibly some cursing is involved here, too.

    Since these side-effects are hard to understand that error message is printed. Unfortunately, it's not immediately obvious what you should be doing if you DO want to have a copy, even though that duplication code requires only one additional method call and isn't called Copy() or Duplicate() but Instantiate() with a given existing object - same as you do with prefabs.
     
    Last edited: Oct 26, 2022
    Madgvox likes this.
  9. Steven-1

    Steven-1

    Joined:
    Sep 11, 2010
    Posts:
    451
    Oh, I didn't know you could do that! ( Instantiate(mesh) I mean )
    Well that solves it, that's exactly what I need!
    Thanks!
    (gonna try it later, don't have time now)

    Edit: Yep, that works
    rereading the previous posts, I notice I somehow missed that Kurt-Dekker's code also used Instantiate(mesh), I thought he was just using MeshFilter.mesh to make a copy.
    Sorry about that, and thanks for the help!
     
    Last edited: Oct 30, 2022