Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 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 Hayden-S · Dec 22, 2018 at 03:39 PM · meshverticesperformance optimizationprocedural meshupdating

Procedural Mesh Editing Performance

Background:

I needed to make a mesh grid that I could change the height (y-axis) of individual quads at any coordinate I specified. I have the mesh created and am able to do exactly what I need to.

Problem:

I need to move many quads simultaneously. When I modify en mass, eg 100, (which is little compared to the many I intend on moving at once) my performance drops heavily.

My grid is made up of multiple meshes, so I find the relevant gameobject using the x and z coordinate and then edit the vertices in that gameobjects mesh

My code snippets:

  private void GroupChange()
     {
         foreach (TileGroupClass group in groupList)
         {
             //if the last tiles height in the group does not equal the new target height, change the height.
             if (group.tileList[group.tileList.Count - 1].y != group.newTargetHeight)
             {
 
                 foreach (Vector3 tile in group.tileList)
                 {
                     ChangeHeight(tile, group);
                 }
             }
         }
     }


  void ChangeHeight(Vector3 tile, TileGroupClass group)
     {
         int x = Mathf.RoundToInt(tile.x);
         int z = Mathf.RoundToInt(tile.z);
         float height = group.newTargetHeight;
 
         float heightChangeSpeed = group.speed;
 
         //convert xz into chunkx and chunkz
         int chunkX;
         if (x >= chunkSize) chunkX = Mathf.RoundToInt(x / chunkSize);
         else chunkX = 0;
 
         int chunkZ = z % chunkSize;
         if (z >= chunkSize) chunkZ = Mathf.RoundToInt(z / chunkSize);
         else chunkZ = 0;
 
         //get the mesh 
         GameObject curChunk = chunkArray[(chunkZ *chunkNum) +chunkX];
         Mesh mesh = curChunk.GetComponent<MeshFilter>().mesh;
 
 
         //set temporary arrays to modify
         Vector3[] vertices  = new Vector3[chunkSize * chunkSize * 4];
         int[] triangles = new int[chunkSize * chunkSize * 6];
 
         vertices = mesh.vertices;
         triangles = mesh.triangles;
 
         //set which quad vertices and triangles to modify
         int changeNum = ((z % chunkSize) * chunkSize) + (x % chunkSize);
         int v = changeNum * 4;
         int t = changeNum * 6;
         
         if(chunkX + chunkZ > 0)Debug.Log(changeNum);
 
         float vertexOffset = cellSize * 0.5f;
         Vector3 cellOffset = new Vector3((x - (chunkX * chunkSize)) * cellSize, 0, (z - (chunkZ * chunkSize)) * cellSize);
 
         //save the quads current vertices position
         Vector3 vertVect1 = new Vector3(vertices[v].x, vertices[v].y, vertices[v].z);
         Vector3 vertVect2 = new Vector3(vertices[v + 1].x, vertices[v + 1].y, vertices[v + 1].z);
         Vector3 vertVect3 = new Vector3(vertices[v + 2].x, vertices[v + 2].y, vertices[v + 2].z);
         Vector3 vertVect4 = new Vector3(vertices[v + 3].x, vertices[v + 3].y, vertices[v + 3].z);
 
         //move the quad to new position
         vertices[v] = Vector3.MoveTowards(vertVect1, new Vector3(-vertexOffset, height, -vertexOffset) + cellOffset + gridOffset, heightChangeSpeed * Time.deltaTime);
         vertices[v + 1] = Vector3.MoveTowards(vertVect2, new Vector3(-vertexOffset, height, vertexOffset) + cellOffset + gridOffset, heightChangeSpeed * Time.deltaTime);
         vertices[v + 2] = Vector3.MoveTowards(vertVect3, new Vector3(vertexOffset, height, -vertexOffset) + cellOffset + gridOffset, heightChangeSpeed * Time.deltaTime);
         vertices[v + 3] = Vector3.MoveTowards(vertVect4, new Vector3(vertexOffset, height, vertexOffset) + cellOffset + gridOffset, heightChangeSpeed * Time.deltaTime);
         
 
         //set triangles
         triangles[t] = v;
         triangles[t + 1] = triangles[t + 4] = v + 1;
         triangles[t + 2] = triangles[t + 3] = v + 2;
         triangles[t + 5] = v + 3;
 
         //set the values into the current mesh 
         mesh.vertices = vertices;
         mesh.triangles = triangles;
         //mesh.RecalculateNormals();
         
 
         Vector3 temp = group.tileList[group.tileList.IndexOf(tile)];
         //group.tileList[group.tileList.IndexOf(tile)].y = vertices[v + 3].y;
 
 
         temp.y = vertices[v + 3].y;
 
         group.tileList[group.tileList.IndexOf(tile)] = temp;
 
         Debug.Log("tile height" + temp.x + " : " + temp.y + " : " + temp.z + " = " + group.tileList.Find(p => p == tile).y);
         //group.curHeight = vertices[v + 3].y;
     }
 

I understand this performance is because I'm updating many things using the update function, and wondered if there is a better way to implement what I'm trying to achieve. I have a great deal still to learn about programming.

Does anyone have any ideas?

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
3
Best Answer

Answer by Bunny83 · Dec 22, 2018 at 04:09 PM

Your main issue probably isn't that you are updating many things, but that you do so extremely inefficient. I'll go through the things as they appear in your code:


First of all this seems to be a bad house keeping solution:

 GameObject curChunk = chunkArray[(chunkZ *chunkNum) +chunkX];
 Mesh mesh = curChunk.GetComponent<MeshFilter>().mesh;

When you know you have to access the MeshFilter very frequently, you should store the references to the required objects in your chunkArray so you don't have to use GetComponent every time. Since you don't really care about the MeshFilter but only about the mesh object you probably should just store the Mesh object reference for each chunk.


The following 4 lines are just crazy:

 Vector3[] vertices  = new Vector3[chunkSize * chunkSize * 4];
 int[] triangles = new int[chunkSize * chunkSize * 6];
 
 vertices = mesh.vertices;
 triangles = mesh.triangles;

The first 2 lines are completely useless and all they do is allocate a huge amount of memory which becomes immediately garbage since you overwrite the two arrays with the generated arrays from the mesh. So step one improvement which will reduce the generated garbage by half: Replace those 4 lines with those 2:

 Vector3[] vertices  = mesh.vertices;
 int[] triangles = mesh.triangles;

Though this will still allocate those two array each time you execute your method. Again if you know you want to manipulate the vertices very often, don't read the arrays each time, but store the arrays along with your chunk.


I don't know why so many people do such crazy things:

 Vector3 vertVect1 = new Vector3(vertices[v].x, vertices[v].y, vertices[v].z);
 Vector3 vertVect2 = new Vector3(vertices[v + 1].x, vertices[v + 1].y, vertices[v + 1].z);
 Vector3 vertVect3 = new Vector3(vertices[v + 2].x, vertices[v + 2].y, vertices[v + 2].z);
 Vector3 vertVect4 = new Vector3(vertices[v + 3].x, vertices[v + 3].y, vertices[v + 3].z);
 

Reading vertices[v] will return the vector3 that is stored in the array at index "v". You only access the x component. Then you again read the same vector3 and only read the y component. And once again just to read the z component. Then you run the constructor of Vector3 to create the very exact same vector3 that you get back when you read vertices[v]. What's the point? Vector3 is a value type so when you assign it to another variable you get a copy of it's value. The 4 vector3 variables are also unnecessary since MoveTowards does not change the values you pass in. Actually it can't because the method only receives a copy of the value.

So you can simply do

 vertices[v] = Vector3.MoveTowards(vertices[v], .......


It looks like that the number of quads in your mesh doesn't really change. There's no need to recreate the triangles array every time if it ends up exactly the same.


Finally the absolute killer, those 3 lines:

      Vector3 temp = group.tileList[group.tileList.IndexOf(tile)];
      temp.y = vertices[v + 3].y;
      group.tileList[group.tileList.IndexOf(tile)] = temp;

it looks like "tileList" is an array of Vector3s, is that right? The "IndexOf" method of an array has to iterate through your whole array (half the array on average) to find that particular vector3 value. You actually do this twice for every element in that tileList. So if there are 1000 elements in your array, you essentially doing 1 million vector3 comparisons. if there are 10k elements you do 100 million comparisons. Next thing is your "temp" value that you are reading from your tileList is actually the same as the vectors in "tile". You're searching for the index of an element that is equal to vector3 tile and then you just read that value. So your first line would be the same as just writing

 Vector3 temp = tile;

Boom half the iterations saved.


However you can avoid all those IndexOf stuff by just passing the index of the tile you want to change the height of instead of the actual position. Since you pass the group along you can directly access the vector3 in that tileList by using the index.

So change the inner loop to

 int count = group.tileList.Length;
 for (int i = 0; i < count; i++)
 {
     ChangeHeight(i, group);
 }

And your ChangeHeight method to

 void ChangeHeight(int tileIndex, TileGroupClass group)
 {
     Vector3 tile = group.tileList[tileIndex];
     // [ ... ]


And at the end of your method instead of your temp variable, you can simply do

 tile.y = vertices[v + 3].y;
 group.tileList[tileIndex] = tile;
 

Comment
Add comment · Show 2 · 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
avatar image Hayden-S · Dec 27, 2018 at 02:47 AM 0
Share

Thank you for replying mate! I'm going to have a look through the code and make some serious modifications!

avatar image Hayden-S · Dec 27, 2018 at 03:00 AM 0
Share

I didn't include all the code, but I have a question in regards to the bad housekeeping: As I am calling other meshes, not calling this gameobjects mesh, wouldn't I need to use something along those lines?

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

110 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

Related Questions

Holes in procedural mesh 0 Answers

Set vertex positions aligned to a grid 2 Answers

What's wrong with this simple mesh manipulation? 1 Answer

Getting mesh length (x,z) 1 Answer

How can I remove redundant vertices in custom mesh? 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