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
2
Question by numberkruncher · Mar 18, 2012 at 11:39 AM · editormeshmeshes

Dynamically combine verticies that share the same space

I want to create a simple editor tool that will:

  1. Combine all selected meshes

  2. Automatically weld/combine verticies that are within 0.0001f of one another

  3. Optimise mesh for best performance

  4. Save as prefab for use

I know how to achieve 1/3 and can probably figure out 4. But how can I weld/combine verticies?

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

5 Replies

· Add your reply
  • Sort: 
avatar image
5
Best Answer

Answer by aldonaletto · Mar 23, 2012 at 12:37 AM

You must not share equal vertices when their uv or normals are different, what usually happens at the edges of different faces, or to "close" a curved surface. Consider the Unity cube: it has 24 vertices (4 per face) instead of just 8, because the normals (and uv, I guess) are different at the edges.
In order to do a safer vertex reduction, you should also compare uv and normals of equal vertices. To further complicate matters, there may exist other less popular properties associated to each vertex, like uv2, tangents and colors, which should be compared too for a 100% safe vertex reduction (take a look at the Mesh doc for a complete list).
NOTE: I would also replace the first foreach by a regular for (int k=0;...;...) - I have not found any .NET doc ensuring that the foreach sequence also starts at 0.

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 numberkruncher · Feb 17, 2013 at 05:10 PM 0
Share

Thank you for clarifying that for me. $$anonymous$$y intention was not to reduce the number of vertices for improved performance, but rather to remove certain visual artefacts that occur on some joins. In Blender the process of welding solves the problem. $$anonymous$$y scene is procedurally generated at design time.

avatar image aldonaletto · Feb 18, 2013 at 03:20 AM 1
Share

You could simply set vertices within some limit to the same value, like this:

 for (int i = 0; i < vertices.Length; i++){
   Vector3 curVert = vertices[i]; // get vertex [i]
   // compare it to the next vertices:
   for (int j = i+1; j < vertices.Length; j++){
     // if any one inside limit distance...
     if (Vector3.Distance(curVert, vertices[j]) < 0.0001f){
       vertices[j] = curVert; // make it equal to vertex [i]
     }
   }
 }

This approach would simply eli$$anonymous$$ate possible precision errors, without modifying the vertices count. $$anonymous$$aybe the same should be applied to the UV pairs, since UV seams are very visible.

avatar image
12

Answer by boxiness · Mar 18, 2014 at 02:25 AM

Numberkruncher I modified your code to use uniform spatial partitioning. Now it should be like 100 times faster on large meshes. Hope this can help someone.

   public static void AutoWeld (Mesh mesh, float threshold, float bucketStep) {
     Vector3[] oldVertices = mesh.vertices;
     Vector3[] newVertices = new Vector3[oldVertices.Length];
     int[] old2new = new int[oldVertices.Length];
     int newSize = 0;
 
     // Find AABB
     Vector3 min = new Vector3 (float.MaxValue, float.MaxValue, float.MaxValue);
     Vector3 max = new Vector3 (float.MinValue, float.MinValue, float.MinValue);
     for (int i = 0; i < oldVertices.Length; i++) {
       if (oldVertices[i].x < min.x) min.x = oldVertices[i].x;
       if (oldVertices[i].y < min.y) min.y = oldVertices[i].y;
       if (oldVertices[i].z < min.z) min.z = oldVertices[i].z;
       if (oldVertices[i].x > max.x) max.x = oldVertices[i].x;
       if (oldVertices[i].y > max.y) max.y = oldVertices[i].y;
       if (oldVertices[i].z > max.z) max.z = oldVertices[i].z;
     }
 
     // Make cubic buckets, each with dimensions "bucketStep"
     int bucketSizeX = Mathf.FloorToInt ((max.x - min.x) / bucketStep) + 1;
     int bucketSizeY = Mathf.FloorToInt ((max.y - min.y) / bucketStep) + 1;
     int bucketSizeZ = Mathf.FloorToInt ((max.z - min.z) / bucketStep) + 1;
     List<int>[,,] buckets = new List<int>[bucketSizeX, bucketSizeY, bucketSizeZ];
 
     // Make new vertices
     for (int i = 0; i < oldVertices.Length; i++) {
       // Determine which bucket it belongs to
       int x = Mathf.FloorToInt ((oldVertices[i].x - min.x) / bucketStep);
       int y = Mathf.FloorToInt ((oldVertices[i].y - min.y) / bucketStep);
       int z = Mathf.FloorToInt ((oldVertices[i].z - min.z) / bucketStep);
 
       // Check to see if it's already been added
       if (buckets[x, y, z] == null)
         buckets[x, y, z] = new List<int> (); // Make buckets lazily
 
       for (int j = 0; j < buckets[x, y, z].Count; j++) {
         Vector3 to = newVertices[buckets[x, y, z][j]] - oldVertices[i];
         if (Vector3.SqrMagnitude (to) < threshold) {
           old2new[i] = buckets[x, y, z][j];
           goto skip; // Skip to next old vertex if this one is already there
         }
       }
 
       // Add new vertex
       newVertices[newSize] = oldVertices[i];
       buckets[x, y, z].Add (newSize);
       old2new[i] = newSize;
       newSize++;
 
     skip:;
     }
 
     // Make new triangles
     int[] oldTris = mesh.triangles;
     int[] newTris = new int[oldTris.Length];
     for (int i = 0; i < oldTris.Length; i++) {
       newTris[i] = old2new[oldTris[i]];
     }
     
     Vector3[] finalVertices = new Vector3[newSize];
     for (int i = 0; i < newSize; i++)
       finalVertices[i] = newVertices[i];
 
     mesh.Clear();
     mesh.vertices = finalVertices;
     mesh.triangles = newTris;
     mesh.RecalculateNormals ();
     mesh.Optimize ();
   }
Comment
Add comment · Show 10 · 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 magic9cube · Jul 04, 2014 at 02:12 PM 0
Share

this is really useful. thanks! i plugged it into my experimental marching cubes engine. any idea how i can stop it removing verts on the edge of chunks?

[1]: /storage/temp/28677-$$anonymous$$r.jpg

tear.jpg (79.6 kB)
avatar image magic9cube · Jul 04, 2014 at 02:28 PM 0
Share

come to think of it.. i really need a solution that matches the normals up across the separate meshes where vertices are shared. this will only work across a single mesh i guess.

avatar image Grimward · Nov 16, 2014 at 09:42 PM 0
Share

boxiness you code works really well. Would be great if you could provide code with possibility to update uv's as well.

avatar image bhansalideepika · Dec 02, 2014 at 06:52 AM 0
Share

what should be the values for threshold and bucketstep?

avatar image jvhgamer · Jul 05, 2017 at 05:54 PM 0
Share

@numberkruncher Thanks so much for this! I'm actively trying to use this function but I am not exactly certain how threshold and bucketStep work together. For example, if I've both = .03 I can reduce a 12k vertice mesh to 1.7k vertices. If I change bucketStep to .04, the result is 1.4k vertices. And again if I change bucketStep to .01, the result is 1.7k. So you can see my confusion as changing the value in both directions doesn't seem to make sense. I want to adjust these values to keep more vertices depending on the size of my models. Please respond and advise. Thanks!

avatar image numberkruncher jvhgamer · Jul 05, 2017 at 06:32 PM 0
Share

@jvhgamer no idea since I didn't write that version of the answer ;)

avatar image jvhgamer numberkruncher · Jul 06, 2017 at 02:05 PM 0
Share

@numberkruncher @boxiness What does threshold do in your version? Boxiness what does bucketStep do in your version? I really want to understand these functions but I'm not sure if these cubic buckets have any effect on the quality of the mesh, or if it is just the threshold. I've been experimenting with the values back and forth to find a repeatable pattern. I've had success but no luck in fully understanding what is going on. I'm still lost.

Show more comments
Show more comments
avatar image
6

Answer by numberkruncher · Mar 22, 2012 at 09:44 PM

I think the answer is that this is not feasible. I have worked out how to write this logic and have points 1/3/4 working great. Here is the code that I wrote for step 2.

Whilst this does in fact weld the verticies together, it screws up the UV coordinates because it seems that there can only be one UV coordinate pair for each vertex. This has had the effect of (properly mapped->mirrored->stretched->mirrored->properly mapped->...)

If anybody knows how to solve this problem then that would be fantastic. Still, here is the welding code that I wrote should it be of any use to anybody:

     private void AutoWeld(Mesh mesh, float threshold) {
         Vector3[] verts = mesh.vertices;
         
         // Build new vertex buffer and remove "duplicate" verticies
         // that are within the given threshold.
         List<Vector3> newVerts = new List<Vector3>();
         List<Vector2> newUVs = new List<Vector2>();
         
         int k = 0;
         
         foreach (Vector3 vert in verts) {
             // Has vertex already been added to newVerts list?
             foreach (Vector3 newVert in newVerts)
                 if (Vector3.Distance(newVert, vert) <= threshold)
                     goto skipToNext;
             
             // Accept new vertex!
             newVerts.Add(vert);
             newUVs.Add(mesh.uv[k]);
             
         skipToNext:;
             ++k;
         }
         
         // Rebuild triangles using new verticies
         int[] tris = mesh.triangles;
         for (int i = 0; i < tris.Length; ++i) {
             // Find new vertex point from buffer
             for (int j = 0; j < newVerts.Count; ++j) {
                 if (Vector3.Distance(newVerts[j], verts[ tris[i] ]) <= threshold) {
                     tris[i] = j;
                     break;
                 }
             }
         }
         
         // Update mesh!
         mesh.Clear();
         mesh.vertices = newVerts.ToArray();
         mesh.triangles = tris;
         mesh.uv = newUVs.ToArray();
         mesh.RecalculateBounds();
     }
Comment
Add comment · Show 6 · 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 Lexoka · Jul 04, 2013 at 04:05 PM 0
Share

That was useful to me, thanks (I don't need the UV coordinates anyway).

However, mesh.vertices = newVerts.ToArray(); generates an error because Unity apparently checks the number of vertices in mesh.vertices against the number of vertices referenced in mesh.triangles.

I suspect this check was not performed in previous versions of Unity, which is why the script used to work, but doesn't anymore.

Anyway, the fix was simple, you just have to do mesh.triangles = tris; before mesh.vertices = newVerts.ToArray(); ins$$anonymous$$d of after.

avatar image numberkruncher · Jul 04, 2013 at 04:47 PM 0
Share

I am not sure why this was not working for you, but according to the Unity documentation the order in my answer is still correct: http://docs.unity3d.com/Documentation/ScriptReference/$$anonymous$$esh.html

avatar image numberkruncher · Jul 04, 2013 at 04:50 PM 0
Share

You may just need to clear the mesh, I have updated my answer accordingly :)

avatar image frogsbo · Mar 07, 2014 at 03:39 AM 0
Share

Thank you, I am vaguely familiar with all this because I write procedural mash, I have found that using procedural volumetric texture shaders is a very nice workaround because it looks very original, and rewriting UVs is difficult, I wrote a box projection script, but it struggles with shared vertices, so much work!

Here is a JavaScript version without UVs

 function  AutoWeld( mesh:$$anonymous$$esh,  threshold:float) {
        var verts : Vector3[]= mesh.vertices;
        var tris:int[]  = mesh.triangles;
        // Build new vertex buffer and remove "duplicate" verticies
        // that are within the given threshold.
 
        var newVerts : List.<Vector3> = new List.<Vector3>();
        var newUVs : List.<Vector2> = new List.<Vector2>();
        
 
        for (var i=0;i<verts.Length;i++) {
         for (var j=0;j<verts.Length;j++) {
             if (verts[i] == verts[j] && i != j ){
 
             newVerts.Add(verts[i]);
             }
           }
         }
         
        // Rebuild triangles using new verticies
         Debug.Log("Is mouse_position null? " + (newVerts.Count ));
        for ( i=0;i<tris.Length;i++) {
  
          for ( j=0;j<newVerts.Count;j++) {
             if (newVerts[j] == verts[ tris[i] ]) {
               tris[i] = j;
               break;
             }
          }
        }
   //print ("tis different" + newVerts.Count + "--" + tris.Length);
        // Update mesh!
         //mesh.Clear();
         mesh.vertices =newVerts.ToArray();
         mesh.triangles = tris;
        // mesh.uv = newUVs.ToArray();
         mesh.RecalculateNormals();
     } 
     
     
     
 @script RequireComponent ($$anonymous$$eshFilter);
avatar image Orion · Mar 13, 2014 at 05:56 PM 0
Share

I have tried implementing this, but it turned out as unacceptably slow. In the end I used a kd-tree to store and search the vertices, which, in my case, was 50x faster (that's not an exaggeration).

Show more comments
avatar image
-2

Answer by kilik128 · Mar 19, 2014 at 01:39 AM

is Uv destroy for my try but vertex working

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
avatar image
0

Answer by BergOnTheJob · Jul 10, 2020 at 02:48 PM

I found this tool, the Oasis Mesh Editor, that can Split Vertices and Merge Vertices back together, https://assetstore.unity.com/packages/slug/166155

The Merge Verts tool also has a Max Tolerance setting so that only verts that are closer than this distance will be merged.

Hope this helps anyone looking =)

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

13 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

Related Questions

Dynamic Vertex Welding 1 Answer

Why do prefabbed meshes go missing whenever I pull an update from Unity Collab? 0 Answers

How to deform a mesh at runtime? 0 Answers

Paint on object in editor and return as texture. 1 Answer

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


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