Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 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
0
Question by Chimera3D · Feb 09, 2014 at 08:44 AM · meshoptimizesubdivisionsplitting

Optimizing Mesh Splitting Code

I have written some code that generates a simple quad, then I borrowed code from the UnifyCommunity that subdivides meshes and keeps the vertices shared, then modified it to subdivide a very specific way, and finally I added another static function that splits the mesh into four separate meshes. The function that splits the subdivided quad runs fine up until the quad has more than 1k vertices, then it's lack of optimization begins to really show. Basically I want to know how I can get my split function to run far faster than it does as I will have to be splitting several high vertex count meshes frequently. Code to reproduce what I am doing can be found below.

Mesh Controller Script: Add this code to an empty gameObject and assign a material for the objectMaterial variable. A quad will be generated in play mode and the "u" key can be used to subdivide the quad and the "i" key can be used to split it.

 using UnityEngine;
 using System.Collections;
 
 public class MeshController : MonoBehaviour {
 
     public Material objectMaterial; //assign a material here
     
     void Start () {
 
         if(transform.parent == null){
 
             gameObject.AddComponent("MeshRenderer");
             gameObject.AddComponent("MeshFilter");
             gameObject.AddComponent("MeshCollider");
             
             Mesh mesh = new Mesh();
             
             Vector3[] vertices = new Vector3[]
             {
                 new Vector3(1, 0, 1),
                 new Vector3(1, 0, -1),
                 new Vector3(-1, 0, 1),
                 new Vector3(-1, 0, -1),
             };
             
             Color[] colors = new Color[]
             {
                 Color.white,
                 Color.white,
                 Color.white,
                 Color.white,
             };
             
             Vector2[] uv = new Vector2[]
             {
                 new Vector2(1, 1),
                 new Vector2(1, 0),
                 new Vector2(0, 1),
                 new Vector2(0, 0),
             };
             
             int[] triangles = new int[]
             {
                 0, 1, 2,
                 2, 1, 3,
             };
             
             Vector3[] normals = new Vector3[ vertices.Length ];
             for( int n = 0; n < normals.Length; n++ )
                 normals[n] = Vector3.up;
             
             mesh.vertices = vertices;
             mesh.normals = normals;
             mesh.colors = colors;
             mesh.uv = uv;
             mesh.triangles = triangles;
             
             GetComponent<MeshFilter>().mesh = mesh;
             GetComponent<MeshCollider>().mesh = mesh;
 
             gameObject.renderer.material = objectMaterial;
 
         }
     
     }
 
     void Update () {
 
         if(Input.GetKeyDown("u")){
 
             MeshSubdiv.Subdivide(gameObject.GetComponent<MeshFilter>().mesh);
 
         }
 
         if(Input.GetKeyDown("i")){
 
             if(GetComponent<MeshRenderer>().enabled == true){
 
                 Mesh[] splitFaces = new Mesh[4];
                 splitFaces = MeshSubdiv.Split(gameObject.GetComponent<MeshFilter>().mesh);
 
                 for(int i = 0; i < 4; i++){
 
                     GameObject newFace = new GameObject();
                     newFace.transform.position = transform.position;
                     newFace.transform.rotation = transform.rotation;
                     newFace.name = "Face" + (i +1);
                     newFace.transform.parent = gameObject.transform;
                     newFace.AddComponent("MeshRenderer");
                     newFace.AddComponent("MeshFilter");
                     
                     newFace.GetComponent<MeshFilter>().mesh = splitFaces[i];
                     
                     newFace.AddComponent("MeshController");
                     
                     newFace.renderer.material = gameObject.renderer.material;
                     newFace.AddComponent("MeshCollider");
                     newFace.GetComponent<MeshCollider>().mesh = splitFaces[i];
 
                 }
 
                 GetComponent<MeshRenderer>().enabled = false;
                 GetComponent<MeshCollider>().enabled = false;
                 //gameObject.tag = ("Untagged");
 
             }
         }
     }
 }

MeshSubdiv Script: This script is where all the subdivision and splitting happens. The "Split" function is where things get slow, after some testing I think that using "list.IndexOf" and "list.Contains" are what's making it slow.. A quad will be generated in play mode and the "u" key can be used to subdivide the quad and the "i" key can be used to split it.

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 
 public static class MeshSubdiv
 {
     static List<Vector3> vertices;
     static List<Vector3> normals;
     static List<Color> colors;
     static List<Vector2> uv;
     static List<Vector2> uv1;
     static List<Vector2> uv2;
     static List<int> indices;
     static Dictionary<uint,int> newVectices;
     static Dictionary<int,int> newVectices2;
 
     static void InitArrays(Mesh mesh) //for subdivision
     {
         vertices = new List<Vector3>(mesh.vertices);
         normals = new List<Vector3>(mesh.normals);
         colors = new List<Color>(mesh.colors);
         uv  = new List<Vector2>(mesh.uv);
         uv1 = new List<Vector2>(mesh.uv1);
         uv2 = new List<Vector2>(mesh.uv2);
         indices = new List<int>();
     }
 
     static void InitArrays2(Mesh mesh) //for mesh splitting
     {
         vertices = new List<Vector3>(mesh.vertices);
         normals = new List<Vector3>(mesh.normals);
         colors = new List<Color>(mesh.colors);
         uv  = new List<Vector2>(mesh.uv);
         uv1 = new List<Vector2>(mesh.uv1);
         uv2 = new List<Vector2>(mesh.uv2);
         indices = new List<int>(mesh.triangles);
     }
 
     static void CleanUp()
     {
         vertices = null;
         normals = null;
         colors = null;
         uv  = null;
         uv1 = null;
         uv2 = null;
         indices = null;
     }
 
     static int GetNewVertex(int i1, int i2)
     {
         int newIndex = vertices.Count;
         uint t1 = ((uint)i1 << 16) | (uint)i2;
         uint t2 = ((uint)i2 << 16) | (uint)i1;
         if (newVectices.ContainsKey(t2))
             return newVectices[t2];
         if (newVectices.ContainsKey(t1))
             return newVectices[t1];
         
         newVectices.Add(t1,newIndex);
         
         vertices.Add((vertices[i1] + vertices[i2]) * 0.5f);
         if (normals.Count>0)
             normals.Add((normals[i1] + normals[i2]).normalized);
         if (colors.Count>0)
             colors.Add((colors[i1] + colors[i2]) * 0.5f);
         if (uv.Count>0)
             uv.Add((uv[i1] + uv[i2])*0.5f);
         if (uv1.Count>0)
             uv1.Add((uv1[i1] + uv1[i2])*0.5f);
         if (uv2.Count>0)
             uv2.Add((uv2[i1] + uv2[i2])*0.5f);
         
         return newIndex;
     }
 
     public static void Subdivide(Mesh mesh)
     {
         newVectices = new Dictionary<uint,int>();
         
         InitArrays(mesh);
         
         int[] triangles = mesh.triangles;
         for (int i = 0; i < triangles.Length; i += 6)
         {
             int i0 = triangles[i + 0];
             int i1 = triangles[i + 1];
             int i2 = triangles[i + 2];
             int i3 = triangles[i + 3];
             int i4 = triangles[i + 4];
             int i5 = triangles[i + 5];
             
             int m0 = GetNewVertex(i0, i1);
             int m1 = GetNewVertex(i1, i2);
             int m2 = GetNewVertex(i0, i2);
             int m3 = GetNewVertex(i3, i4);
             int m4 = GetNewVertex(i4, i5);
             int m5 = GetNewVertex(i3, i5);
 
             indices.Add(m0);   indices.Add(i1);   indices.Add(m1);
             indices.Add(m3);   indices.Add(i4);   indices.Add(m4);
 
             indices.Add(m3);   indices.Add(m4);   indices.Add(m5);
             indices.Add(m5);   indices.Add(m4);   indices.Add(i5);
 
             indices.Add(i0);   indices.Add(m0);   indices.Add(m2);
             indices.Add(m2);   indices.Add(m0);   indices.Add(m1);
 
             indices.Add(m2);   indices.Add(m1);   indices.Add(i2);
             indices.Add(i3);   indices.Add(m3);   indices.Add(m5);
 
         }
         mesh.vertices = vertices.ToArray();
         if (normals.Count > 0)
             mesh.normals = normals.ToArray();
         if (colors.Count>0)
             mesh.colors = colors.ToArray();
         if (uv.Count>0)
             mesh.uv = uv.ToArray();
         if (uv1.Count>0)
             mesh.uv1 = uv1.ToArray();
         if (uv2.Count>0)
             mesh.uv2 = uv2.ToArray();
         
         mesh.triangles = indices.ToArray();
         
         CleanUp();
     }
 
     //Mesh Split Function
 
     public static Mesh[] Split(Mesh mesh)
     {
         InitArrays2(mesh);
         Mesh[] meshList = new Mesh[4];
 
         for(int n = 0; n < 4; n++){
 
             List<Vector3> newVertices = new List<Vector3>();
             List<Vector3> newNormals = new List<Vector3>();
             List<Color> newColors = new List<Color>();
             List<Vector2> newUv = new List<Vector2>();
             List<Vector2> newUv1 = new List<Vector2>();
             List<Vector2> newUv2 = new List<Vector2>();
             List<int> newIndices = new List<int>();
 
             newVectices2 = new Dictionary<int,int>();
 
             for(int i = 0 + (n * (indices.Count/4)); i < ((indices.Count/4) * (n + 1)); i += 6){
 
                 for(int x = 0; x < 6; x++){
 
                     int curIndex = indices[i+x];
 
                     if(!newVectices2.ContainsKey(curIndex)){
 
                         newVertices.Add (vertices[indices[i+x]]);
                         newNormals.Add (normals[indices[i+x]]);
                         //newColors.Add (colors[indices[i+x]]); no mesh colors
                         newUv.Add (uv[indices[i+x]]);
                         //newUv1.Add (uv1[indices[i+x]]); no uv1
                         //newUv2.Add (uv2[indices[i+x]]); no uv2
                         newIndices.Add (newVertices.Count-1);
                         newVectices2.Add (curIndex, newVertices.Count-1);
 
                     }else{
 
                         newIndices.Add ((int)newVectices2[curIndex]);
 
                     }
 
                 }
 
             }
 
             Mesh newMesh = new Mesh();
 
             newMesh.vertices = newVertices.ToArray();
 
             if(newNormals.Count > 0)
                 newMesh.normals = newNormals.ToArray();
             if(newColors.Count > 0)
                 newMesh.colors = newColors.ToArray();
             if(newUv.Count > 0)
                 newMesh.uv = newUv.ToArray();
             if(newUv1.Count > 0)
                 newMesh.uv1 = newUv1.ToArray();
             if(newUv2.Count > 0)
                 newMesh.uv2 = newUv2.ToArray();
 
             newMesh.triangles = newIndices.ToArray();
 
             meshList[n] = newMesh;
 
         }
 
         CleanUp();
 
         return meshList;
 
     }
 
     //Mesh Split Function
     
     public static Mesh DuplicateMesh(Mesh mesh)
     {
         return (Mesh)UnityEngine.Object.Instantiate(mesh);
     }
 
 }
         
Comment
Add comment · Show 5
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
avatar image Chimera3D · Feb 09, 2014 at 06:05 PM 0
Share

I might also add that the reason I have my splitting code set up the way it is, is so that I don't have duplicated vertices. If I removed the if statement and the else condition it would work fast enough but I would have way more vertices.

avatar image Kiloblargh · Feb 09, 2014 at 06:22 PM 1
Share

You're right that IndexOf and Contains are slow, (they're basically looping through every item and checking if it is the right one- and it might even be faster if you just did that explicitly with a for loop and put a break in it ) but I am not sure how to get rid of them.

I think all you can do is try to come up with a smarter script which already knows the index of what you're getting the IndexOf, (using another Dictionary?) and makes sure the list that already only Contains what it is supposed to contain, before you get to that point.

avatar image Chimera3D · Feb 09, 2014 at 07:13 PM 0
Share

I tried using an explicit loop and it sped up a little bit, but it was still not adequate. Then I tried using a dictionary as $$anonymous$$iloblargh said and it's a lot faster now. I still don't think it's fast enough though, so I'm wondering if using Vector3's as dictionary keys is slower than using ints. If it is, then how could I generate a unique id for each Vector3 that could be reproduced whenever that exact same Vector3 has to be processed through the unique id generator?

Edit:

Wait, how could I just produce an integer from a Vector3 by appending the values on the three axes together? For example: x = 1, y = 2, z =3, int = 123.

Edit 2:

The above won't work simply because of negative values. I'll try it with strings ins$$anonymous$$d of floats (ints wouldn't work as well).

Edit 3

I'm not trying to spam or anything but using integers for the dictionary keys isn't noticeably faster, I've updated my code above and it uses the vertex index in the original mesh as the "unique id." At this point I've exhausted all of my ideas. Help?

avatar image Chimera3D · Mar 09, 2014 at 08:24 PM 0
Share

Okay this is a continuation of this question, how could I adapt my code above, the mesh splitting function, to keep the indices properly ordered after splitting. By 'properly' I mean that if a quad on subdivision level 4 is split (with the resultant ones on level 3) then the indices will differ from one that was subdivided to level 3. Ins$$anonymous$$d I want the indices to be the same. But I still want it to have shared vertices.

avatar image Chimera3D · Mar 10, 2014 at 03:39 AM 0
Share

Really, can anyone help me with this?

1 Reply

· Add your reply
  • Sort: 
avatar image
1

Answer by Meridium · Jan 28, 2015 at 01:57 PM

There are a couple of things you can do to improve your performance.

1) You are creating new lists with InitArrays.
You should replace this with List.Clear(). This prevents further memory allocations every time you initialize.
Also you should not destroy then on cleanup if you want to reuse them.

2) You can replace the ContainsKey with TryGetValue. Its a slightly faster, but can have huge impact on performance depending on the number of hit/misses, and number of calls.
More info here: http://stackoverflow.com/questions/9382681/what-is-more-efficient-dictionary-trygetvalue-or-containskeyitem

3) newFace.name = "Face" + (i +1);
Probably unnoticeable, but since every tick counts... contatenating strings takes some toll on memory allocation.
(that statement is actually creating 2 extra strings)
You can replace with: String.Format("Face{0}", (i+1))

4) for(int i = 0 + (n (indices.Count/4)); i < ((indices.Count/4) (n + 1)); i += 6)
You should remove the calculations from the loop:

int fromVal = (n (indices.Count/4));
int toVal = ((indices.Count/4)
(n + 1));
for(int i = fromVal; i < toVal; i += 6)...

5) On your Split function, you have: int curIndex = indices[i+x];
but you are not using it in every indices[i+x]

EDIT: just noticed that every time you: (something[i1] + something[i2]) * 0.5f can be replaced by the much faster: (something[i1] + something[i2]) >> 1

These all add up on your performance.
I hope this helps!

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

19 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

Related Questions

Subdivide mesh C#... help 0 Answers

Is it possible to subdivide mesh with catmull-clark inside Unity? 2 Answers

Can you apply smoothing to split vertices on a generated mesh? 1 Answer

Scripting Meshes Subdivision(Javascript) 0 Answers

Uneven mesh yet heights are all equal to one. 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