Search Unity

Octree subdivision problem [solved]

Discussion in 'Scripting' started by phort99, Jan 16, 2010.

  1. phort99

    phort99

    Joined:
    Oct 20, 2009
    Posts:
    76
    Update: solution below. You can skip this boring spiel.

    I'm trying to create a script for subdividing space into an octree for levels of detail. I'm currently stuck on this block of code.

    The problem I've been having is that it works fine for the first subdivision, but for subsequent subdivisions it seems to duplicate objects as their own children, or something, so instead of subdividing once into 8 equal sized sections, I wind up with several single subdivisions (one instead of eight for any subdivision beyond the first).

    This doesn't happen if I use the block of code in there that destroys all children of the newly created instance, but it still only creates one child instead of eight after the first split. I do know that my condition for splitting is working (divides if the camera is close enough).

    If you're curious, the condition for subdivision is basically:
    Code (csharp):
    1. if (distance = 1024 / ceil(level of detail+1)) so it uses LOD 0 if distance > 1024, 1 if distance > 512, 2 if distance > 341.33, etc.
    Here's the relevant code block:
    Code (csharp):
    1. else if (thisLOD < Mathf.CeilToInt(1024f / (cameraPosition - center).magnitude) - 1)
    2. {
    3.     isCurrentLOD = false;
    4.     renderer.enabled = false;
    5.    
    6.     if (transform.childCount == 0)
    7.     {
    8.         for (int i = 0; i < 8; i++)
    9.         {
    10.             Vector3 instancePosition = transform.position;
    11.             instancePosition.x += halfScale.x * octreePositions[i].x;
    12.             instancePosition.y += halfScale.y * octreePositions[i].y;
    13.             instancePosition.z += halfScale.z * octreePositions[i].z;
    14.            
    15.             GameObject newObject = UnityEngine.Object.Instantiate
    16.                 (subtreePrefab) as GameObject;
    17.            
    18.             if (newObject.transform.childCount != 0)
    19.             {
    20.                 Transform[] newObjectChildren = newObject.GetComponentsInChildren<Transform>();
    21.                 IEnumerator childrenEnum = newObjectChildren.GetEnumerator();
    22.                 while (childrenEnum.MoveNext())
    23.                 {
    24.                     UnityEngine.Object.Destroy((childrenEnum.Current as Transform).gameObject, 0f);
    25.                 }
    26.             }
    27.            
    28.             newObject.transform.parent = transform;
    29.            
    30.             newObject.transform.position = instancePosition;
    31.            
    32.             // since this is local scale, it is multiplied by all its ancestors' scales so
    33.             // it only needs to be 1/2.
    34.             newObject.transform.localScale = new Vector3(.5f,.5f,.5f);
    35.             newObject.renderer.enabled = true;
    36.  
    37.         }
    38.     }
    39. }
    40.  
    Here are some possible clues as to why this is happening. Hopefully you can come up with a better way to do this.

    My "inheritance" setup is:
    Cube
    octree script attached
    octree script has a public field which takes a GameObject
    I created a prefab of this cube, and then dragged that prefab from the library onto the gameobject field in itself (in theory creating a recursive relationship where it can spawn a copy of itself)
    I broke the prefab connection with the instantiated cube. If I don't do this, the first instance does the same single-division thing as well.
    I dragged the prefab onto the gameobject field in the instantated cube.

    Sorry this is so confusing! Please give me any ideas you have, I've been struggling with this the past few nights. Thank you!
     
  2. phort99

    phort99

    Joined:
    Oct 20, 2009
    Posts:
    76
    I found out the hard way that instantiation isn't the right solution for this problem because it replicates children too!

    If your object is an instance of a prefab, and you try to instantiate the prefab, it will just dupe the current object.

    My solution was to just hard code the creation of the object which wasn't that bad. Here is the equivalent (now-working) block of code as before:

    Code (csharp):
    1. else if (thisLOD < Mathf.CeilToInt(1024f / (cameraPosition - center).magnitude) - 1)
    2.         {
    3.             isCurrentLOD = false;
    4.             renderer.enabled = false;
    5.            
    6.             if (transform.childCount == 0)
    7.             {
    8.                 for (int i = 0; i < 8; i++)
    9.                 {
    10.                     Vector3 instancePosition = transform.position;
    11.                     instancePosition.x += halfScale.x * octreePositions[i].x;
    12.                     instancePosition.y += halfScale.y * octreePositions[i].y;
    13.                     instancePosition.z += halfScale.z * octreePositions[i].z;
    14.                    
    15.                     BuildNewNode(instancePosition);
    16.                 }
    17.             }
    18.         }
    19.     }
    20. }
    21.  
    22. GameObject BuildNewNode(Vector3 position)
    23. {
    24.     GameObject newNode = new GameObject("Terrain Detail Submesh", typeof(MeshFilter), typeof(EtCetera));
    25.    
    26.     newNode.transform.position = position;
    27.     newNode.transform.parent = this.transform;
    28.    
    29.     // since this is local scale, it is multiplied by all its ancestors' scales automatically, so
    30.     // it only needs to be 1/2.
    31.     newNode.transform.localScale = new Vector3(.5f,.5f,.5f);
    32.    
    33.     return newNode;
    34. }
    I found out after I got it working that my formula for increasing detail wasn't sound anyway... I accidentally flew my camera too close and wound up with 1.4M triangles! I'll have to either change my constant multiplier or use a different falloff curve.

    PRETTY PICTURES.
     

    Attached Files:

  3. Tinus

    Tinus

    Joined:
    Apr 6, 2009
    Posts:
    437
    Nice (and thanks for posting)! This could be a lovely little jumpstart for when I eventually get to LOD scaling in my own game. Mind if I use it? :eek:
     
  4. phort99

    phort99

    Joined:
    Oct 20, 2009
    Posts:
    76
    Tinus: Okay! I stripped my octree implementation down to a skeleton. Now it should be very easily extensible; just read the in-line comments to figure out where to put your code.

    Usage: Attach it to an empty GameObject. The comments in the code should explain it better than I will bother here.
     

    Attached Files:

  5. Tinus

    Tinus

    Joined:
    Apr 6, 2009
    Posts:
    437
    You. Are. Awesome. Thanks a bunch! :eek:


    I don't know if the UnifyWiki contains any LOD scripts yet, but you should probably post it on there too. :)
     
  6. Neodrop

    Neodrop

    Joined:
    Oct 24, 2008
    Posts:
    1,359
    What this line mean ?

    Line (124) : g.GetComponent<TerrainOctree().IsCurrentLOD = false;

    TerrainOctree not found.

    Line (96) Vector3 parentCenter; - need to be initialised. For example, like this :

    Vector3 parentCenter = Vector3.zero;
     
  7. Neodrop

    Neodrop

    Joined:
    Oct 24, 2008
    Posts:
    1,359
    I think, you can replace this code :

    Code (csharp):
    1. // There doesn't seem to be any other way to easily retrieve an object's children than by getting
    2.                     // their transform components
    3. IEnumerator children = transform.GetComponentsInChildren<Transform>().GetEnumerator();
    4. while (children.MoveNext())
    5. {
    6.    GameObject g = (children.Current as Transform).gameObject;
    7.    g.GetComponent<Octree>().IsCurrentLOD = true;
    8. }
    with this one :

    Code (csharp):
    1.  
    2. Octree[] children = transform.GetComponentsInChildren<Octree>();
    3. int count = children.Length;
    4. for (int i = 0; i < count; i++)children[i].isCurrentLOD = true;
    5.  
    There is a little bit improved version.
     

    Attached Files: