Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 14 Next capture
2021 2022 2023
2 captures
13 Jun 22 - 14 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by Animo1985 · Feb 11 at 10:39 PM · generationmeshesprocedural meshquadstructure

Unity Mesh Generation from a Quadtree

Hi,

I am trying to implement a procedurally generated (near-infinite) terrain on a Quad-Sphere in Unity. Therefor I already managed to create Plane meshes, map them to a sphere and now I'm trying to implement some LOD via a quadtree structure. I already coded a (very basic) quadtree implementation but now I can't wrap my head around how to (recursively) generate a mesh from the leaf nodes (children) and how to calculate triangle and vertex indices recursively.


I'm sure it's not a big deal, but I'm quite new to this and can't really wrap my head around this mesh generation stuff.


It would be so nice, if someone can give me kind of a clue, at least where I can find answers on how to generate meshes from quadtrees. Thank you in advance.

My quadtree Implementation looks as follows:


QuadTreeManager.cs

 [ExecuteInEditMode]
 public class QuadTreeManager : MonoBehaviour
 {
     public QuadTree quadTree;
     public Transform observer;
     public Vector3 observerPositionLastFrame;
     // This is the edge-length for one "side" of one subdivision quad
     public int leafGridSize = 256;
     public int maxTreeDepth = 8;
     private List<Vector3> meshVertices = new List<Vector3>();
     public void Init()
     {
         InitializeQuadTree();
     }
     private void InitializeQuadTree()
     {
         quadTree = new QuadTree(Vector3.zero, leafGridSize, 0, maxTreeDepth);
     }
     private void Update()
     {
         if (null != observer && null != quadTree)
         {
             quadTree.UpdateBasedOnProximity(observer);
 
             if (quadTree.isDirty())
             {
                 meshVertices = quadTree.GetMeshVertices();
                 // Then the geometry needs an update
             }
             else
             {
                 // Just for debug: Foreach points in List draw a line to see if coordinates are correct 
                 foreach (var coordinate in meshVertices)
                 {
                     Debug.DrawLine(coordinate, coordinate + Vector3.up);
                 }
             }
         }
         observerPositionLastFrame = observer.position;
     }
     private void OnDrawGizmos()
     {
         if (null == quadTree)
         {
             Init();
             return;
         }
         quadTree.OnDrawGizmo();
     }
 }

QuadTree.cs

 public class QuadTree
 {
     public QuadTree[] children;
     public QuadTree parent = null;
     public float leafGridSize;
     public Bounds bounds;
     public int myDepth;
     public int maxDepth;
     public Vector3 coords;
     public bool verticesChanged = true;
     private Color myGizmoColor = Color.white;
     public bool isSubdivided = false;
     public QuadTree(Vector3 coords, float leafGridSize, int myDepth = 0, int maxDepth = 1)
     {
         this.leafGridSize = leafGridSize;
         this.myDepth = myDepth;
         this.coords = coords;
         this.maxDepth = maxDepth;
         bounds = new Bounds(coords, Vector3.one * leafGridSize * 2f);
     }
     public void UpdateBasedOnProximity(Transform observer)
     {
         var interactionObject = observer.GetComponent<QuadTreeInteractionObject>();
         var isClose = false;
 
         if (null != interactionObject)
         {
             if (bounds.Intersects(interactionObject.bounds))
                 isClose = true;
         }
         else
         {
             if (bounds.Contains(observer.position))
                 isClose = true;
         }
         if (isClose)
         {
             Subdivide();
         }
         else
         {
             RecycleChildren();
             return;
         }
         if (isClose && isSubdivided)
         {
             for (var i = 0; i < children.Length; i++)
                 children[i].UpdateBasedOnProximity(observer);
         }
     }
 
     public List<Vector3> GetMeshVertices()
     {
         List<Vector3> vertices = new List<Vector3>();
         vertices.AddRange(GetMyVertices());
         if (null != children)
         for (var i = 0; i < children.Length; i++)
         {
             {
                 var childVertices = children[i].GetMeshVertices();
                 vertices.AddRange(childVertices);
             }
         }
         verticesChanged = false;
         return vertices;
     }
 
     List<Vector3> GetMyVertices()
     {
         var vertices = new List<Vector3>
         {
             // Top Left
             new Vector3(coords.x - leafGridSize, 0, coords.z - leafGridSize),
             // Top Right
             new Vector3(coords.x + leafGridSize, 0, coords.z - leafGridSize),
             // Bottom Left
             new Vector3(coords.x - leafGridSize, 0, coords.z + leafGridSize),
             // Bottom Right
             new Vector3(coords.x + leafGridSize, 0, coords.z + leafGridSize)
         };
         return vertices;
     }
 
     public bool isDirty()
     {
         var childGeometryChanged = verticesChanged;
         if (children != null)
         {
             for (var i = 0; i < children.Length; i++)
                 childGeometryChanged |= children[i].isDirty();
         }
         return childGeometryChanged;
     }
 
     private void RecycleChildren()
     {
         if (children != null)
         {
             for (var i = 0; i < children.Length; i++)
             {
                 children[i].RecycleChildren();
                 verticesChanged = true;
             }
         }
         children = null;
         isSubdivided = false;
     }
 
     public void Subdivide ()
     {
         if (!isSubdivided)
         {
             if (myDepth <= maxDepth)
             {
                 children = new QuadTree[4];
 
                 // NW:
                 children[0] = new QuadTree(new Vector3(coords.x - leafGridSize / 2f, 0, coords.z + leafGridSize / 2f), leafGridSize / 2f, myDepth + 1,
                     maxDepth);
 
                 // NE: 
                 children[1] = new QuadTree(new Vector3(coords.x + leafGridSize / 2f, 0, coords.z + leafGridSize / 2f), leafGridSize / 2f, myDepth + 1,
                     maxDepth);
 
                 // SW: 
                 children[2] = new QuadTree(new Vector3(coords.x + leafGridSize / 2f, 0, coords.z - leafGridSize / 2f), leafGridSize / 2f, myDepth + 1,
                     maxDepth);
 
                 // SE:
                 children[3] = new QuadTree(new Vector3(coords.x - leafGridSize / 2f, 0, coords.z - leafGridSize / 2f), leafGridSize / 2f, myDepth + 1,
                     maxDepth);
 
                 for (var i = 0; i < 4; i++)
                     children[i].parent = this;
 
                 verticesChanged = true;
                 isSubdivided = true;
             }
         }
     }
     public void OnDrawGizmo()
     {
         if (null != children)
         {
             foreach( QuadTree child in children )
                 child?.OnDrawGizmo();
         }
         var b = bounds.size;
         b.y = .5f;
         Gizmos.color = myGizmoColor;
         Gizmos.DrawWireCube(coords, b);
     }
 }


Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image
0

Answer by BastianUrbach · Feb 13 at 12:14 PM

You make a recursive method that receives a vertex and triangle list. If the node is a leaf, it adds its geometry to the lists and returns. If it's an inner node, it may or may not add geometry to the lists depending on your requirements but more importantly, it invokes the same method with the same parameters on its children so that they can add their geometry. It's not fundamentally different from your OnDrawGizmo method.

A node can simply write its own vertices unchanged, without caring about the rest of the tree. However, when a node writes indices (triangles), it must respect the offset of its vertices in the list. This isn't hard to do, you just store the number of vertices before you add your own and add that offset to each index.

So basically, the method would look somewhat like this:

 void GenerateGeometry(List<Vector3> vertices, List<int> triangles) {
     var indexOffset = vertices.Count;
 
     if (children == null) {
         // Add leaf geometry to the lists
         // To write a position, simply do vertices.Add(position)
         // To write an index, do triangles.Add(indexOffset + index)
     } else {
         // Add inner node geometry to the lists
         // (optional, inner nodes may not have geometry)
         // To write a position, simply do vertices.Add(position)
         // To write an index, do triangles.Add(indexOffset + index)
 
         // Invoke recursively
         foreach (var child in children) {
             child.GenerateGeometry(vertices, triangles);
         }
     }
 }
Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

134 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Dynamically move vertices of a mesh 2 Answers

Why does Frame Selected toggle between position and bounds for procedural and imported meshes? 1 Answer

How can I remove redundant vertices in custom mesh? 0 Answers

Procedural Mesh Generation: Creating a spherical sector 2 Answers

Problems Generate a Chunk blocks (Space Game Infinity) 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges