Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Setting Meshes to keep their positions after Mesh CombineMeshes ?

Discussion in 'Scripting' started by ANTROPO, Jan 8, 2011.

  1. ANTROPO

    ANTROPO

    Joined:
    May 26, 2010
    Posts:
    51
    Hi,

    I am wondering how combining meshes with Mesh.CombineMeshes(CombineInstance) handles original mesh positions,
    as in my script, I am "merging" my individually generated faces and groups of faces (walls, walls with doors, walls with windows basically) into rooms. The walls are already generated in correct positions, but after combining them, they are all placed in the rooms container (parent object) local 0,0,0 point with 0 rotation. And therefore they are overlapping and are not visible unless I set SubMeshes on when calling CombineMeshes so one submesh becomes visible.

    Here's my personalised script:

    Code (csharp):
    1.  
    2.  
    3.                         cell.AddComponent(MeshFilter);
    4.             cell.AddComponent("MeshRenderer");
    5.            
    6.             var cellmeshComponent : MeshFilter = cell.GetComponent(MeshFilter) as MeshFilter;
    7.             var cellmesh : Mesh = cellmeshComponent.mesh;
    8.            
    9.             var childTransform : Transform;
    10.            
    11.             var meshFilters : List.<MeshFilter>;
    12.             meshFilters = new List.<MeshFilter>();
    13.  
    14.             for (var child : Transform in cell.transform) {
    15.                 meshFilters.Add(child.gameObject.GetComponent(MeshFilter) as MeshFilter);
    16.                 //child.position += cell.transform.position;
    17.             }
    18.                 //cell.transform.position = Vector3.zero;
    19.                 //cell.transform.rotation = Quaternion.identity;
    20.            
    21.                         //this line somehow always results in an error, so I have manually created a list of child mesh filters
    22.                         //in the loop above
    23.             //var meshFilters = cell.GetComponentsInChildren(MeshFilter) as MeshFilter[];
    24.            
    25.             var combine : CombineInstance[] = new CombineInstance[meshFilters.Count];
    26.            
    27.             for (comb = 0; comb < meshFilters.Count; comb++) {
    28.                
    29.                   combine[comb].mesh = meshFilters[comb].sharedMesh;               
    30.                   combine[comb].transform = meshFilters[comb].transform.localToWorldMatrix;
    31.                   meshFilters[comb].renderer.enabled = false;
    32.  
    33.                     }
    34.             cellmesh.CombineMeshes(combine, false, false);
    35.             cell.renderer.material = meshFilters[1].renderer.sharedMaterial;
    36.  
    37.  
    I understand that the combine[index].transform is probably responsible for the position of individual meshes,
    although I do not understand how I can set it to keep its original position (if it is possible of course), whether it will be with Matrix4x4 or some other way.

    Any pointers will be greatly appreciated ;)
     
  2. ANTROPO

    ANTROPO

    Joined:
    May 26, 2010
    Posts:
    51
    The combined mesh already in its current overlapped state gives me a measurable performance increase, so this is probably very useful indeed :) although it looks bad
     

    Attached Files:

  3. ANTROPO

    ANTROPO

    Joined:
    May 26, 2010
    Posts:
    51
    I am looking at it right now and have found out that when i actually set the useMatrices
    from
    function CombineMeshes (combine : CombineInstance[], mergeSubMeshes : bool = true, useMatrices : bool = true) :
    to true and this line of the script is carried out:

    combine[comb].transform = meshFilters[comb].transform.localToWorldMatrix;

    the individual meshes do keep their positions in relation to each other (they are not "flattened"), although the new mesh kind of moves from its original position anyway
    (this will take some time to figure out how the Matrix4x4 works)

    but what bothers me, is that the faces of the mesh are visible only in Scene Render Paths view, and in Textured view, only a wireframe is shown, which also seems to be showing only wires of faces that are currently oriented towards camera, as if it was of course a real mesh (the wire also shows only when the object is selected - it is selectable in the scene view, although invisible)

    after it has been selected, the mesh in some angles flicks and shows a real textured mesh it is supposed to be.

    I will try to figure out what messes up my positions, but does anyone have any ideas about the combined mesh?
    some things that i forgot to do to the mesh after it has been combined?

    Oh my, :) Thanks even if you just read this guys.
     
  4. ANTROPO

    ANTROPO

    Joined:
    May 26, 2010
    Posts:
    51
    Code (csharp):
    1.  
    2.  
    3. Mesh.Optimize();
    4.  
    5.  
    Oh well...:rolleyes:
     
  5. ale870

    ale870

    Joined:
    Apr 21, 2009
    Posts:
    149
    Hello,

    I have the same problem: I create 100 cubes, and they are child of a "master" cube. They are correctly positioned in the space.
    But as soon as I apply Optimize() method, they are all "moved" to a single plane!
    This is my code:

    Code (csharp):
    1. var prefab : Transform;
    2.  
    3. function Update () {
    4.     if(Input.GetKeyDown(KeyCode.X)) {
    5.         var minMax = 30;
    6.  
    7.         for(x=-minMax; x<minMax; x++) {
    8.             var res = Instantiate(prefab, Vector3(0, 0, 0), Quaternion.identity);
    9.  
    10.             for(z=-minMax; z<minMax; z++) {
    11.                 var resChild = Instantiate(prefab, Vector3(x, Random.Range(0, 20), z), Quaternion.identity);
    12.                 resChild.parent = res;
    13.             }
    14.  
    15.             res.GetComponent(MeshFilter).Optimize();
    16.         }
    17.        
    18.     }
    19. }
    Please can you help me?
     
    AldeRoberge likes this.
  6. ANTROPO

    ANTROPO

    Joined:
    May 26, 2010
    Posts:
    51
    Hi, that means you have some kind of oposite problem, because Optimize() was just my solution to the flicker of geometry. The positioning had to be figured out by observation, to understand how the objects get positioned in my case.

    I dont understand from your example why are you trying to Optimize each individual res objects meshfilter after just Instantiating it, when all it does is just basically loads the object, no modifications to geometry have been made. Parenting objects != Combining meshes.

    Does this code actually work? Parenting is done via transform.
     
  7. MFKJ

    MFKJ

    Joined:
    May 13, 2015
    Posts:
    264
  8. s2403353662

    s2403353662

    Joined:
    Apr 16, 2022
    Posts:
    1
    I provide an alternative solution. That is to maintain the pre-combination and post-combination positions by calculating and updating the center point position.
    So before you combine your meshs, calculate your meshs center position:
    Code (CSharp):
    1.    public void updateCenterPosition()
    2.     {
    3.         Vector3 sum = Vector3.zero;
    4.        
    5.  
    6.         foreach (Transform child in transform)
    7.         {
    8.             sum += child.position;
    9.         }
    10.         centerPosition = sum / transform.childCount;
    11.     }
    save the center position you will use it while combining your meshs, now combine your meshs:
    Code (CSharp):
    1.   public void ConvertToMergeMesh()
    2.     {
    3.         previousPosition = transform.position;
    4.         MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
    5.         Material[] materials = new Material[meshFilters.Length];
    6.         CombineInstance[] combineInstances = new CombineInstance[meshFilters.Length];
    7.  
    8.         for (int i = 0; i < meshFilters.Length; i++)
    9.         {
    10.             materials[i] = meshFilters[i].GetComponent<MeshRenderer>().sharedMaterial;
    11.  
    12.             combineInstances[i].mesh = meshFilters[i].sharedMesh;
    13.             combineInstances[i].transform = meshFilters[i].transform.localToWorldMatrix;
    14.             // combineInstances[i].transform = Matrix4x4.identity;
    15.             meshFilters[i].gameObject.SetActive(false);
    16.         }
    17.  
    18.         Mesh combinedMesh = new Mesh();
    19.         combinedMesh.CombineMeshes(combineInstances, true, true);
    20.  
    21.         combinedMesh = CalculateCenterPoint(combinedMesh);
    22.            
    23.         MeshFilter meshFilter = gameObject.AddComponent<MeshFilter>();
    24.         meshFilter.sharedMesh = combinedMesh;
    25.  
    26.         MeshRenderer meshRenderer = gameObject.AddComponent<MeshRenderer>();
    27.         meshRenderer.sharedMaterials = materials;
    28.  
    29.         MeshCollider thisCollider = gameObject.AddComponent<MeshCollider>();
    30.         thisCollider.sharedMesh = combinedMesh;
    31.  
    32.         gameObject.transform.position = centerPosition;
    33.  
    34.         // thisCollider.;
    35.     }
    36. private Mesh CalculateCenterPoint(Mesh _mesh)
    37. {
    38.     // Step 1: Get the vertices of the mesh
    39.     Vector3[] vertices = _mesh.vertices;
    40.  
    41.     // Step 2: Calculate the center point of the vertices
    42.     Vector3 center = Vector3.zero;
    43.     foreach (Vector3 vertex in vertices)
    44.     {
    45.         center += vertex;
    46.     }
    47.  
    48.     center /= vertices.Length;
    49.  
    50.     // Step 3: Move vertices to the new center
    51.     for (int i = 0; i < vertices.Length; i++)
    52.     {
    53.         vertices[i] -= center;
    54.     }
    55.  
    56.     // Step 4: Update the mesh with the new vertices
    57.     _mesh.vertices = vertices;
    58.     _mesh.RecalculateBounds();
    59.  
    60.     return _mesh;
    61. }
    62.  
    63.     public void DeconvertToMultiMesh()
    64.     {
    65.         MeshFilter _meshFilter = gameObject.GetComponent<MeshFilter>();
    66.  
    67.         if (_meshFilter)
    68.         {
    69.             Destroy(_meshFilter);
    70.         }
    71.  
    72.         MeshRenderer _meshRenderer = gameObject.GetComponent<MeshRenderer>();
    73.         if (_meshRenderer)
    74.         {
    75.             Destroy(_meshRenderer);
    76.         }
    77.  
    78.         MeshCollider _meshCollider = gameObject.GetComponent<MeshCollider>();
    79.         if (_meshCollider)
    80.         {
    81.             Destroy(_meshCollider);
    82.         }
    83.  
    84.         foreach (Transform child in transform)
    85.         {
    86.             child.gameObject.SetActive(true);
    87.         }
    88.         transform.position = previousPosition;
    89.     }
    function CalculateCenterPoint is the most important part. it calculate the vertical center position of your combined meshs. so let the combined mesh's vertical center position = centerPosition, you will find that your mesh will not change position before and after combining.
    Just forget localToWorldMatrix or Matrix4x4.identity, this method works well.
     
  9. Invinciblade

    Invinciblade

    Joined:
    Mar 13, 2021
    Posts:
    2
    In my case, I (and the unity example, boooo) neglected to apply the parent transform to the Combine transform.

    The unity example has this line...

    combine[i].transform = meshFilters[i].transform.localToWorldMatrix;


    ...which results in offsetting the mesh vertices by the parent transform value. That is, if you set your parent object's position to [0, 0, 100], your vertices will actually render at [0, 0, 200] (100 units away from your parent object, which is 100 units away from the origin).

    The fix for me was to modify that line to the below instead, applying the inverse of the parent's transform to each of the combine transforms. This makes it so that each of the combine meshes treat the parent object's transform (which my combine script is attached to) as their origin:

    combine[i].transform = meshFilters[i].transform.localToWorldMatrix[B] * transform.worldToLocalMatrix[/B];


    Cheers!