Search Unity

Calculating a bound of a grouped model

Discussion in 'Scripting' started by kimsama, Aug 18, 2011.

  1. kimsama

    kimsama

    Joined:
    Jan 2, 2009
    Posts:
    166
    I'm trying to calculate bounding box of a grouped model. The model has several child gameobject which has MeshFilter for its mesh but parent model has not. (The parent gameobject is just an empty gameobject)

    root model - a simple empty gameobject
    |- child A - a model which has MeshFilter
    |- child B - same as child A

    What I want to do is that adding BoxCollider which's bounds are tight enough to fit with both of childeren to the root gameobject. So after getting bounds:

    root model - contains BoxCollider which's bounds is big enough to includes both of child A and child B
    |- child A - a model which has MeshFilter
    |- child B - same as child A

    There are already a few post about it but nothing of them work for me.

    http://answers.unity3d.com/questions/49860/creating-a-bounding-box-for-multiple-objects-using.html
    http://answers.unity3d.com/questions/26909/object-oriented-bounding-boxes-for-multiple-meshes.html
    http://answers.unity3d.com/questions/38417/calculating-the-object-bounds-of-the-group-of-obje.html

    I tried like the followings:

    Code (csharp):
    1.  
    2.     public static void calcBound(GameObject root)
    3.     {
    4.         foreach (Transform child in root.transform)
    5.         {
    6.             if (!child.gameObject.GetComponent<BoxCollider>())
    7.                 child.gameObject.AddComponent<BoxCollider>();
    8.  
    9.         }
    10.  
    11.         BoxCollider[] boxCollider = root.GetComponentsInChildren<BoxCollider>();
    12.        
    13.         Bounds bounds = new Bounds(Vector3.zero, Vector3.zero);
    14.  
    15.         foreach (BoxCollider bc in boxCollider)
    16.         {
    17.             bounds.Encapsulate(bc.bounds);
    18.         }
    19.  
    20.         Debug.Log("BoxCollider bounds: " + bounds.ToString());
    21.     }
    22.  
    But bounds of the root gameobject does not change. By the way I ran the above code in EditorWindow.

    Any ideas?
     
  2. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    I'm assuming that the children also have renderers attached. If so, you could set it up like this:
    TotalBounds.cs
    Code (csharp):
    1. private Bounds bounds;
    2.  
    3.  
    4. void Start ()
    5. {
    6.     bounds = new Bounds (transform.position, Vector3.one);
    7.     Renderer[] renderers = GetComponentsInChildren<Renderer> ();
    8.     foreach (Renderer renderer in renderers)
    9.     {
    10.         bounds.Encapsulate (renderer.bounds);
    11.     }
    12. }
     
    olavrv, Boodums, arufolo and 4 others like this.
  3. kimsama

    kimsama

    Joined:
    Jan 2, 2009
    Posts:
    166
    Sadly, your example code does not work for that.

    Created bounds are too much bigger than what it is expected.
     
    CloudyVR likes this.
  4. AngryAnt

    AngryAnt

    Keyboard Operator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    You'll have to elaborate a bit on that for further assistance.
     
  5. NicolasHognon

    NicolasHognon

    Joined:
    Nov 15, 2010
    Posts:
    32
    The question is old but perhaps the problem comes from the initialization of the bounds.

    Code (csharp):
    1.  bounds = new Bounds (transform.position, Vector3.one);
    I used
    Code (csharp):
    1.  
    2.  bounds = new Bounds (transform.position, Vector3.zero);
    instead and it seems ok.
     
    Boodums and Fangh like this.
  6. serjabc

    serjabc

    Joined:
    Sep 24, 2012
    Posts:
    1
    Code (csharp):
    1. bounds.Encapsulate (renderer.bounds);
    it doesn't work correctly if children were rotated
     
    Last edited: Apr 16, 2013
  7. drudiverse

    drudiverse

    Joined:
    May 16, 2013
    Posts:
    218
    Hi There, can you confirm that
    1. bounds.Encapsulate (renderer.bounds);
    doesnt actually work in Unity3d if the bounds are rotated?

    is there a way of visualising some bounds?

    I was going to just go through all verctices of all childres and get largest and smalles x,y,z.
     
  8. drudiverse

    drudiverse

    Joined:
    May 16, 2013
    Posts:
    218
    ... 1 hour later :D


    function mybounds()//rotate all childs to look at gobject central axis, yodamod 123 decides X, Y, Z decides which axis
    {
    var px = -10000.0;
    var py = -10000.0;
    var pz = -10000.0;
    var mx = 10000.0;
    var my = 10000.0;
    var mz = 10000.0;

    go = Selection.activeGameObject;

    var childs = go.GetComponentsInChildren(Transform);
    for (var ws = 0; ws<childs.length; ws++)
    { //EVERY CHILDS


    if( childs[ws].GetComponent(MeshFilter))
    {
    mt = childs[ws].GetComponent(MeshFilter).mesh;
    var v1 = mt.vertices;
    for ( i =0; i < mt.vertices.length; i++)
    {
    var v2 = childs[ws].transform.position + v1 ;
    print (go.transform.TransformPoint(v1));
    if ( v2.x > px ) { px = v2.x; }

    if ( v2.y > py ) { py = v2.y; }
    print (v2.y +" py " + py);
    if ( v2.z > pz ) { pz = v2.z; }
    if ( v2.x < mx ) { mx = v2.x; }
    if ( v2.y < my ) { my = v2.y; }
    if ( v2.x < mz ) { mz = v2.x; }
    }
    }
    }
    var extents1 = Vector3( ( px - mx ) , ( py - my ) , ( pz - mz ) );
    var center1 = Vector3( ( px/2 + mx ) , ( py/2 + my ) , ( pz/2 + mz ) );
    print (center1 +" extentes " + extents1);
    }

    /// havent checked it over, take away the print instructions and maybe double check the transformpoint information relative to intended bounds maths.
     
  9. drudiverse

    drudiverse

    Joined:
    May 16, 2013
    Posts:
    218
    Dammit my code doesnt work for parents with many childs, because i have to resize and reposition the vertex for every child position and size that contains the vertex, which can be as many as 3. its a hassle.
     
  10. drudiverse

    drudiverse

    Joined:
    May 16, 2013
    Posts:
    218
    petey likes this.
  11. tgouala-wellfiredLtd

    tgouala-wellfiredLtd

    Joined:
    Jun 8, 2013
    Posts:
    99
    Here a snippet to get the local bounds relative to the model parent transform which works for any model, no matter it is rotated or not :
    Code (CSharp):
    1.  
    2. private void CalculateLocalBounds()
    3.      {
    4.          Quaternion currentRotation = this.transform.rotation;
    5.          this.transform.rotation = Quaternion.Euler(0f,0f,0f);
    6.          Bounds bounds = new Bounds(this.transform.position, Vector3.zero);
    7.          foreach(Renderer renderer in GetComponentsInChildren<Renderer>())
    8.          {
    9.              bounds.Encapsulate(renderer.bounds);
    10.          }
    11.          Vector3 localCenter = bounds.center - this.transform.position;
    12.          bounds.center = localCenter;
    13.          Debug.Log("The local bounds of this model is " + bounds);
    14.          this.transform.rotation = currentRotation;
    15.      }
     
    Fangh, petey, Ian094 and 1 other person like this.
  12. Sackstand

    Sackstand

    Joined:
    May 26, 2015
    Posts:
    10
    hi, and sorry for Negro this Thread. but the last Solution will not work if you have assets like DoTween or ITween running. it will break the whole tween! please keep that in mind. I took me hours to find it out :(
     
  13. fffMalzbier

    fffMalzbier

    Joined:
    Jun 14, 2011
    Posts:
    3,276
    Very useful to generate a Boxcollider for click interactions, saves a lot of time. :)
     
  14. naryk86

    naryk86

    Joined:
    May 28, 2017
    Posts:
    4
    Hello!
    Could you please help me?
    I tried to understand and to use what you wrote here but it seems to fail....

    I use the following code to calc bounds of prefab instantiated runtime in a 2d infinite runner game, placing clones of prefabs next to each other.
    The code seems it "does its job" when PREFAB is gameobject with a mesh renderer, made of several sprites (for example TERRAIN).

    But if PREFAB is an empty game object with mesh renderer, made of children prefab (each one made of several sprites, for example: TERRAINnextToWATERnextToTERRAIN) where each prefab has a mesh renderer attached.... the code fails to identify bounds correctly.



    private Bounds CalcPrefabBounds(Transform obj)
    {
    // First find a center for your bounds.
    Vector3 center = Vector3.zero;

    foreach (Transform child in obj.transform)
    {
    center += child.gameObject.GetComponent<Renderer>().bounds.center;
    }
    center /= obj.transform.childCount; //center is average center of children

    //Now you have a center, calculate the bounds by creating a zero sized 'Bounds',
    Bounds bounds = new Bounds(center, Vector3.zero);

    foreach (Transform child in obj.transform)
    {
    bounds.Encapsulate(child.gameObject.GetComponent<Renderer>().bounds);
    }

    return bounds;


    }

    ("obj" is a PREFAB)

    Could you help me?

    Thanks in advance
     
  15. tabulatouch

    tabulatouch

    Joined:
    Mar 12, 2015
    Posts:
    23
    It works but you need also to remove scale from the computation:

    public void CalculateLocalBounds()
    {
    Quaternion currentRotation = this.transform.rotation;
    Vector3 currentScale = this.transform.localScale;
    this.transform.rotation = Quaternion.Euler(0f, 0f, 0f);
    this.transform.localScale = Vector3.one;
    Bounds bounds = new Bounds(this.transform.position, Vector3.zero);
    foreach (Renderer renderer in GetComponentsInChildren<Renderer>())
    {
    bounds.Encapsulate(renderer.bounds);
    }
    Vector3 localCenter = bounds.center - this.transform.position;
    bounds.center = localCenter;

    Debug.Log("The local bounds of this model is " + bounds);
    this.transform.rotation = currentRotation;
    this.transform.localScale = currentScale;
    boxcoll.center = bounds.center;
    boxcoll.size = bounds.size;
    }
     
  16. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
    It's unfortunate that the code example above is COMPLETELY WRONG.

    You CANNOT initialize a Bounds using "Vector3.zero"

    That does make a tiny bounds at that point. When you then encapsulate more renderers, it will include that point - and that origin may be very distant from the boxes.

    There's no such thing as an "empty" Bounds, you must create it with the "first" one.

    The correct code is trivial ...

    Code (CSharp):
    1.         Transform t ... the overall game object in question
    2.  
    3.         Renderer[] rr = t.GetComponentsInChildren<Renderer>();
    4.         Bounds b = rr[0].bounds;
    5.         foreach ( Renderer r in rr ) { b.Encapsulate(r.bounds); }
    (Obviously, handle the case where the function is mistakenly passed an object with no renderers.)