I have a scene where I’m trying to combine multiple GameObject’s into a single GameObject and I’m having some troubles with the Mesh.
I’m currently using the CombineMesh function to create the new GameObject’s Mesh but I’m having troubles when trying to combine Spheres.
Each time I’m using the standard CombineMesh functionality, I keep getting 'Cooking::cookConvexMesh: user-provided hull must have less than 256 vertices!" errors.
From what I understand, MeshFilter’s act as the GameObject’s mesh for components like material, ‘how it does physics’, etc.
My theory was, I’ll create a Mesh for the MeshFilter so my actual material and outline of the object looks good, but I’ll swap out the collider mesh with something that has a lower vertices count for the collider part. Each time I experiment with this, it appears that MeshFilter & MeshCollider share the same mesh.
I’m able to create a working mesh for the MeshCollider that seems to be legit, except every time I apply it, the GameObject comes out distored (the material is applied to the MeshCollider mesh instead of what I was hoping was my MeshFilter mesh)
using UnityEngine;
using System.Collections;
using VRStandardAssets.Utils;
public class CombineChildren : MonoBehaviour {
/**
* Find all the child objects of this object and combine their meshes
* into a new object, destroying the old objects. Note that this object
* should itself not have a mesh as it would be combined into that mesh
* as well, and this object would be destroyed.
**/
public void Combine() {
MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter> ();
CombineInstance[] combineInstances = new CombineInstance[meshFilters.Length];
CombineInstance[] combineCollider = new CombineInstance[meshFilters.Length];
for (var i = 0; i < meshFilters.Length; i++) {
if (meshFilters [i].transform == transform) {
continue;
}
combineInstances[i].mesh = meshFilters[i].sharedMesh;
combineInstances[i].transform = meshFilters[i].transform.localToWorldMatrix;
if (combineInstances [i].mesh.name == "Sphere Instance") {
combineCollider[i].mesh = Instantiate(Resources.Load("Meshes/Octahedron Sphere 2 D1")) as Mesh;
} else {
combineCollider [i].mesh = meshFilters [i].sharedMesh;
}
combineCollider [i].subMeshIndex = 0; // Just in case
combineCollider [i].transform = meshFilters [i].transform.localToWorldMatrix;
}
GameObject obj = new GameObject("SuperModel", typeof(MeshFilter), typeof(MeshRenderer), typeof(Rigidbody));
obj.AddComponent<VRInteractiveItem> ();
obj.AddComponent<Grabbable> ();
obj.AddComponent<Combinable> ();
obj.AddComponent<Colourable> ();
obj.transform.parent = transform;
obj.GetComponent<MeshFilter>().mesh = new Mesh();
obj.GetComponent<MeshFilter>().sharedMesh.CombineMeshes(combineInstances);
obj.GetComponent<MeshFilter>().sharedMesh.name = "FilterMesh";
Material objMaterial = meshFilters [0].gameObject.GetComponent<MeshRenderer> ().sharedMaterial;
obj.GetComponent<MeshRenderer>().material = new Material(objMaterial);
obj.AddComponent<MeshCollider>();
obj.GetComponent<MeshCollider> ().sharedMesh.CombineMeshes (combineCollider);
obj.GetComponent<MeshCollider> ().sharedMesh.name = "ColliderMesh";
obj.GetComponent<MeshCollider> ().convex = true;
// Add Highlightable outline now that new Filter has been set
obj.AddComponent<Highlightable>();
// Reset the objects active state. This is important so that we can hook
// all the delegates again.
obj.SetActive (false);
obj.SetActive (true);
// Destroy all the objects
foreach (MeshFilter m in meshFilters) {
Destroy(m.gameObject);
}
}
}
Any suggestions on how to handle this would be appreciated!
If you could post your “OctahedronSphere 2 D1” mesh, I could see if I can spot what’s going awry. In the meantime - you can double-click the mesh in the mesh filter and the mesh collider, and see how it looks in the preview window. That might give you some information.
Based upon the code i’ve included, whichever mesh I include (either the ColliderMesh aka the one with the substitution for sphere or FilterMesh) to my ‘MeshCollider’ component, will be applied to both components. If I put the colliderMesh on I can see it properly in the preview and if i put the filterMesh on I can see it also in preview but my new object falls through my scene but I get an error
Am i wrong in understanding that both MeshFilter and MeshCollider can have their OWN meshes or does a GameObject only have one mesh that is shared amongst all of it’s components?
aaah, thanks, I found your issue. Yes, the mesh filter and mesh collider can have different meshes, but you’re not creating two meshes - you’re just creating one.
When you do AddComponent(), Unity does the same thing as when you add a mesh collider in the inspector - looks for a MeshFilter on the same object, and assigns the same mesh to the MeshCollider as the MeshFilter.
You’ll have to explicitly replace the mesh on the MeshCollider, as you’re doing on the MeshFilter:
//This is what you're doing now:
obj.AddComponent<MeshCollider>(); //Doing this assigns the MeshFilter's mesh as this object's mesh
obj.GetComponent<MeshCollider> ().sharedMesh.CombineMeshes (combineCollider); //Same mesh, so you're overwriting what you did with the mesh filter above
//Do this instead:
obj.AddComponent<MeshCollider>();
obj.GetComponent<MeshCollider>().sharedMesh = new Mesh();
obj.GetComponent<MeshCollider>().sharedMesh.CombineMeshes(combineCollider);
//Actually, do this instead - it reads a lot better. AddComponent returns the object added.
MeshCollder meshCollider = obj.AddComponent<MeshCollider>();
meshCollider.sharedMesh = new Mesh();
meshCollider.sharedMesh.CombineMeshes(combineCollider);
While performance isn’t an issue here, you should be in the habit of not repeatedly doing GetComponent for the same component, as that’s a problem at runtime.
I knew I was close but that there was some behind the scenes stuff that was causing problems for me.
and re: the GetComponent… the notation you indicated on line 11 is pretty cool… it makes sense to be able to manipulate the variable itself without constantly having to fool around with the Getters
Hi @Baste and @reado :
I trying to implement move/rotate/zoom GameObject. And I have one GameObject with multiple child object.
I think, if I want to move/rotate/zoom GameObject, I have to add MeshCollider for GameObject.
So I tried to use your code above to add MeshCollider. But it don’t work for me.
Here’s my code :
GameObject instance = Instantiate (Resources.Load ("Chair2") as GameObject);
// Re-parent the cube as child of the trackable gameObject
instance.transform.parent = theTrackable.transform;
// Adjust the position and scale
// so that it fits nicely on the target
instance.transform.localPosition = new Vector3 (0, 0, 0);
// instance.transform.localRotation = Quaternion.identity;
instance.transform.localScale = new Vector3 (0.1f, 0.1f, 0.1f);
// instance.AddComponent<MeshCollider>();
MeshFilter[] meshFilters = instance.GetComponentsInChildren<MeshFilter> ();
CombineInstance[] combineCollider = new CombineInstance[meshFilters.Length];
Debug.Log ("Length of meshFilters === " + meshFilters.Length);
for (var i = 0; i < meshFilters.Length; i++) {
if (meshFilters [i].transform == transform) {
continue;
}
combineCollider[i].mesh = meshFilters[i].sharedMesh;
combineCollider[i].transform = meshFilters[i].transform.localToWorldMatrix;
Debug.Log ("Name of combineCollider === " + combineCollider[i].mesh.name);
}
MeshCollider meshCollider = instance.AddComponent<MeshCollider>();
meshCollider.sharedMesh = new Mesh();
meshCollider.sharedMesh.CombineMeshes(combineCollider);
// Make sure it is active
instance.SetActive (true);
Thanks a lot for solving the reado’s issue!
I had the same problem and I also was wondering if both MeshFilter and MeshCollider can have their OWN meshes or does a GameObject only have one mesh that is shared amongst all of its components. You have already answered but actually I do not understand how this:
//This is what you're doing now:
obj.AddComponent<MeshCollider>(); //Doing this assigns the MeshFilter's mesh as this object's mesh
obj.GetComponent<MeshCollider> ().sharedMesh.CombineMeshes (combineCollider); //Same mesh, so you're overwriting what you did with the mesh filter above
can differs from:
//Do this instead:
obj.AddComponent<MeshCollider>();
obj.GetComponent<MeshCollider>().sharedMesh = new Mesh();
obj.GetComponent<MeshCollider>().sharedMesh.CombineMeshes(combineCollider);
Is the first wrong because when he set the MeshCollider shared mesh with the combineCollider, he is setting also the mesh of the mesh filter?
What you solved is also feasible for updating a mesh collider (that is different from the mesh filter mesh) in an update function?
Mesh Filter and Mesh Collider does indeed have their own mesh. It’s just that usually they’re the same mesh, as Unity automatically sets the Mesh Filter’s mesh as the colliders mesh.
If you add a Mesh Collider to an object in the editor, or do this in code:
obj.AddComponent<MeshCollider>();
, Unity looks for a MeshFilter on the same gameobject, and if it finds it, assigns that filter’s mesh as the mesh collider’s mesh. So now they’re referencing the same mesh object, and changes to one changes the other.
CombineCollider edits the mesh you’re calling it on directly
You don’t want to do exactly the same thing - in my example, I’m making a completely new mesh and working with that. That’s appropriate for a single-time thing, but you don’t want to do that in Update - that’d cause you to create very many meshes that’ll be discarded soon.
In that case, the appropriate thing is to make a copy once, and then edit the copy directly. So something like:
void Awake() {
//This makes an exact copy of the old mesh collider.
meshCollider.sharedMesh = new Mesh(meshCollider.sharedMesh);
}
void Update() {
//since the copy is unique, you can edit it directly here.
meshCollider.sharedMesh.vertices = newVertices;
}
Thanks again for your support and sorry for my lack of experience, but I’m missing something.
Here my problem (the same of the topic):
I’ve been computing my own Mesh mesh starting from a plane mesh, updating it every frame and it works correctly.
When I attached the same mesh to a Meshcollider component it works also for the collision detection.
Actually I’d need that the Meshcollider has a different mesh.
So I tried to use your advices to add the Meshcollider component directly by the script.
But I still have problems
This is a script attached to a custom asset object which is instantiated by another object. And when I do this:
public float timeSpeed = 1f;
public float collider_offset = 0.0f;
private MeshCollider meshCollider;
private Mesh mesh;
void Start()
{
meshCollider = this.gameObject.AddComponent<MeshCollider>(); // is this what you actually suggested ?
meshCollider.sharedMesh = new Mesh();
meshCollider.material.dynamicFriction = 0.1f;
meshCollider.material.staticFriction = 0.1f;
meshCollider.sharedMesh.MarkDynamic();
mesh = GetComponent<MeshFilter>().mesh;
mesh.MarkDynamic();
}
void Update () {
Vector3[] vertices = mesh.vertices;
Vector3[] vert_temp1 = vertices; //
Vector3[] vert_temp2 = new Vector3[vertices.Length];
int i = 0;
while (i < vertices.Length) {
vert_temp1[i].y = .... // some stupid calculation for mesh (it works). It modifies a simple plane mesh in y dimension
vert_temp2[i].y = vert_temp1[i].y + collider_offset;
i++;
}
mesh.vertices = vert_temp1;
mesh.RecalculateNormals();
meshCollider.sharedMesh.vertices = vert_temp2;
meshCollider.sharedMesh.triangles = mesh.triangles; // it Does not work both with and without this line
Mesh mesh is computed and rendered correctly, while no Meshcollider mesh is computed. The component is present but not the mesh of the collider.
A Meshcollider mesh is set only if the Mesh mesh is used. What I’m doing wrong.
I just missed:
vert_temp2.x = vert_temp1*.x; vert_temp2.z = vert_temp1.z; now it works: thanks a lot! But a warning (reading this Dynamic mesh collider - Questions & Answers - Unity Discussions) is that you need to update an external mesh, compute bounds before attaching it to the meshcollider. You cannot work directly on the sharedMesh _*_</em></em> <em><em><em>* newMesh.vertices = vert_temp2; newMesh.triangles = mesh.triangles; newMesh.RecalculateBounds(); meshCollider.sharedMesh = newMesh;*</em></em></em> <em><em>_**_