Setting Meshes to keep their positions after Mesh CombineMeshes ?

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:

                        cell.AddComponent(MeshFilter);
			cell.AddComponent("MeshRenderer");
			
			var cellmeshComponent : MeshFilter = cell.GetComponent(MeshFilter) as MeshFilter;
			var cellmesh : Mesh = cellmeshComponent.mesh;
			
			var childTransform : Transform;
			
			var meshFilters : List.<MeshFilter>;
			meshFilters = new List.<MeshFilter>();

			for (var child : Transform in cell.transform) {
				meshFilters.Add(child.gameObject.GetComponent(MeshFilter) as MeshFilter);
				//child.position += cell.transform.position;
			}
				//cell.transform.position = Vector3.zero;
				//cell.transform.rotation = Quaternion.identity;
			
                        //this line somehow always results in an error, so I have manually created a list of child mesh filters
                        //in the loop above
			//var meshFilters = cell.GetComponentsInChildren(MeshFilter) as MeshFilter[];
			
			var combine : CombineInstance[] = new CombineInstance[meshFilters.Count];
			
			for (comb = 0; comb < meshFilters.Count; comb++) {
        		
        		  combine[comb].mesh = meshFilters[comb].sharedMesh;        		
        		  combine[comb].transform = meshFilters[comb].transform.localToWorldMatrix;
        		  meshFilters[comb].renderer.enabled = false;

    		        }
    		cellmesh.CombineMeshes(combine, false, false);
    		cell.renderer.material = meshFilters[1].renderer.sharedMaterial;

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 :wink:

The combined mesh already in its current overlapped state gives me a measurable performance increase, so this is probably very useful indeed :slight_smile: although it looks bad

468717--16435--$Screen shot 2011-01-08 at 11.42.21 AM.png

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, :slight_smile: Thanks even if you just read this guys.

Mesh.Optimize();

Oh well…:roll_eyes:

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:

var prefab : Transform;

function Update () {
	if(Input.GetKeyDown(KeyCode.X)) {
        var minMax = 30;

        for(x=-minMax; x<minMax; x++) {
            var res = Instantiate(prefab, Vector3(0, 0, 0), Quaternion.identity);

            for(z=-minMax; z<minMax; z++) {
                var resChild = Instantiate(prefab, Vector3(x, Random.Range(0, 20), z), Quaternion.identity);
                resChild.parent = res;
            }

            res.GetComponent(MeshFilter).Optimize();
        }
	   
	}
}

Please can you help me?

1 Like

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.

I am getting the same Issue how did you resolve it. ? how to retain combine mesh original position. see my http://stackoverflow.com/questions/32731840/combined-mesh-showing-on-different-screen-location-while-position-is-not-changed

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:

   public void updateCenterPosition()
    {
        Vector3 sum = Vector3.zero;
       

        foreach (Transform child in transform)
        {
            sum += child.position;
        }
        centerPosition = sum / transform.childCount;
    }

save the center position you will use it while combining your meshs, now combine your meshs:

  public void ConvertToMergeMesh()
    {
        previousPosition = transform.position;
        MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
        Material[] materials = new Material[meshFilters.Length];
        CombineInstance[] combineInstances = new CombineInstance[meshFilters.Length];

        for (int i = 0; i < meshFilters.Length; i++)
        {
            materials[i] = meshFilters[i].GetComponent<MeshRenderer>().sharedMaterial;

            combineInstances[i].mesh = meshFilters[i].sharedMesh;
            combineInstances[i].transform = meshFilters[i].transform.localToWorldMatrix;
            // combineInstances[i].transform = Matrix4x4.identity;
            meshFilters[i].gameObject.SetActive(false);
        }

        Mesh combinedMesh = new Mesh();
        combinedMesh.CombineMeshes(combineInstances, true, true);

        combinedMesh = CalculateCenterPoint(combinedMesh);
           
        MeshFilter meshFilter = gameObject.AddComponent<MeshFilter>();
        meshFilter.sharedMesh = combinedMesh;

        MeshRenderer meshRenderer = gameObject.AddComponent<MeshRenderer>();
        meshRenderer.sharedMaterials = materials;

        MeshCollider thisCollider = gameObject.AddComponent<MeshCollider>();
        thisCollider.sharedMesh = combinedMesh;

        gameObject.transform.position = centerPosition;

        // thisCollider.;
    }
private Mesh CalculateCenterPoint(Mesh _mesh)
{
    // Step 1: Get the vertices of the mesh
    Vector3[] vertices = _mesh.vertices;

    // Step 2: Calculate the center point of the vertices
    Vector3 center = Vector3.zero;
    foreach (Vector3 vertex in vertices)
    {
        center += vertex;
    }

    center /= vertices.Length;

    // Step 3: Move vertices to the new center
    for (int i = 0; i < vertices.Length; i++)
    {
        vertices[i] -= center;
    }

    // Step 4: Update the mesh with the new vertices
    _mesh.vertices = vertices;
    _mesh.RecalculateBounds();

    return _mesh;
}

    public void DeconvertToMultiMesh()
    {
        MeshFilter _meshFilter = gameObject.GetComponent<MeshFilter>();

        if (_meshFilter)
        {
            Destroy(_meshFilter);
        }

        MeshRenderer _meshRenderer = gameObject.GetComponent<MeshRenderer>();
        if (_meshRenderer)
        {
            Destroy(_meshRenderer);
        }

        MeshCollider _meshCollider = gameObject.GetComponent<MeshCollider>();
        if (_meshCollider)
        {
            Destroy(_meshCollider);
        }

        foreach (Transform child in transform)
        {
            child.gameObject.SetActive(true);
        }
        transform.position = previousPosition;
    }

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.

1 Like

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_.transform = meshFilters*.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<em>.transform = meshFilters<em>.transform.localToWorldMatrix __* transform.worldToLocalMatrix__;
Cheers!