So a while ago a friend of mine, Perry Leijten, asked me to help out with seemingly a simple problem for his awesome skinning tools.
*Which you can find here btw: https://gumroad.com/peerke#
The problem being:
“I want to sort my selection of vertices in groups.”
i.e. all neighboring vertices in separate groups.
Simple enough i thought and had a crack at it.
…
Lots of hair later, 4am, birds chirping and I had a working version… that being immensely slow. After some tinkering and brainstorming we had a version that could do vertex sorting on a 40k vertex mesh in about 1 second.
This got implemented in his skinning tools. Now about 6 months later I revisited the problem and got a speed improvement of about 70%. Sooo I thought it would be nice to make a post about it.
Hopefully the comments in the code speak for themselves.
If not and you have any questions feel free to ask in the comments.
To test it do the following:
1. Create a poly sphere in maya .
2. set it to 200×200 subdivisions
3. run the script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
import maya.OpenMaya as oldOpenMaya import collections import time from maya import mel ## Util needed for the old maya vertex itter. M_SCRIPT_UTIL = oldOpenMaya.MScriptUtil() def getVertexGroups( mesh, vtxSelectionSet): ''' This system finds vertex groups. To solve a problem like this I like to think in analogies to remember the steps a bit better. ^_^ So a vertex is a house number, and the connecting vertices are neighbors. Think of it as neighbors living in houses but the city planing is horrible!. City planning wants to divide the people in districts. But they messed up the house numbers so they dont know who lives near who. So we have have a list of house numbers and we want divide them into groups! i.e. list: 1,24,42,1337,... We will ask house nr 1 if they have a moment to talk about ... their neighbors. We ask on what number their neighbors live and we write everything down in our BIG NOTEBOOK. i.e. So house 1 is connected to neighbor 2,3,10,22 and 42 If one of these neighbors is in our list (Lets say nr 42) We walk over to this neighbor and ask the same question. etc etc. When we no longer find any neighbors that are connected, we tear off the paper from the BIG NOTEBOOK and continue on the next. And call this group district one. Now we go to house nr 2. We check if we already talked to house nr 2 while looking at neighbors!. If we did, we will go to nr3 etc. ''' ## Creates a selection list (doesn't actually get anything yet) selList = oldOpenMaya.MSelectionList() ## Adds the mesh to the list selList.add(mesh) ## Old style maya create an object mObject = oldOpenMaya.MObject() ## Get the dependancy node and put it into the mObject selList.getDependNode(0, mObject) ## Create a vertex itterator loop. iterVertLoop = oldOpenMaya.MItMeshVertex(mObject) ## Empty set to keep track of who we talked to. talkedToNeighbours = set() districtList = [] ## < OUR BIG NOTEBOOK!!! ## For every index in our provided set. Get the connecting / neighboring vertices. for currentIndex in vtxSelectionSet: ## An empty set that holds all house numbers. districtHouses = set() ## If our current index is not in the talked to list. We can process it. if not currentIndex in talkedToNeighbours: ## this nr is part of our list and not seen before. ## so we add it to the district districtHouses.add( currentIndex ) currentNeighbours = getNeighbours(iterVertLoop, currentIndex) ## As long as we have neighbours we need to keep asking around. while currentNeighbours: ## Empty set to keep track of all the new neighbours we are about to find. newNeighbours = set() ## For each neighbour we currently know of. for neighbour in currentNeighbours: ## If they are on our list and we have not talked to them before. if neighbour in vtxSelectionSet and not neighbour in talkedToNeighbours: talkedToNeighbours.add(neighbour) ## Note down we talked to them districtHouses.add(neighbour) ## Add them to our current district. ## Add all their neighbours to the new neighbours list... newNeighbours = newNeighbours.union(getNeighbours(iterVertLoop, neighbour)) ## We are done with asking all the neigbours we knew of ... so now we continue with all the new ones we found. currentNeighbours = newNeighbours districtList.append( districtHouses )## << Write down data in our big notebook return sorted(districtList, key = lambda x:min(x)) def getNeighbours( mVtxItter, index): mVtxItter.setIndex( index, M_SCRIPT_UTIL.asIntPtr()) intArray = oldOpenMaya.MIntArray() mVtxItter.getConnectedVertices(intArray) return set(int(x) for x in intArray) ## First make a poly sphere of 200 Subdivision Axes and 200 Subdivision height. ## then run this script. ## Mock selection of vertecies. mel.eval("select -replace pSphere1.vtx[8000:9000] pSphere1.vtx[10000:12000] pSphere1.vtx[15338:15345] pSphere1.vtx[15538:15545] pSphere1.vtx[15738:15745] pSphere1.vtx[15938:15945] pSphere1.vtx[16138:16145] pSphere1.vtx[16338:16345] pSphere1.vtx[16538:16545] pSphere1.vtx[16738:16745] pSphere1.vtx[16938:16945] pSphere1.vtx[17138:17145] pSphere1.vtx[17338:17345] pSphere1.vtx[17361] pSphere1.vtx[17518:17519] pSphere1.vtx[17538:17545] pSphere1.vtx[17561:17565] pSphere1.vtx[17714:17719] pSphere1.vtx[17761:17768] pSphere1.vtx[17911:17919] pSphere1.vtx[17961:17968] pSphere1.vtx[18107:18119] pSphere1.vtx[18161:18168] pSphere1.vtx[18304:18319] pSphere1.vtx[18361:18367] pSphere1.vtx[18433:18446] pSphere1.vtx[18501:18519] pSphere1.vtx[18561:18567] pSphere1.vtx[18633:18646] pSphere1.vtx[18699:18719] pSphere1.vtx[18761:18767] pSphere1.vtx[18833:18846] pSphere1.vtx[18896:18919] pSphere1.vtx[18961:18967] pSphere1.vtx[19033:19046] pSphere1.vtx[19093:19118] pSphere1.vtx[19164:19167] pSphere1.vtx[19233:19246] pSphere1.vtx[19290:19315] pSphere1.vtx[19367] pSphere1.vtx[19433:19446] pSphere1.vtx[19487:19512] pSphere1.vtx[19633:19646] pSphere1.vtx[19685:19709] pSphere1.vtx[19797:19802] pSphere1.vtx[19833:19846] pSphere1.vtx[19882:19907] pSphere1.vtx[19997:20005] pSphere1.vtx[20033:20046] pSphere1.vtx[20079:20104] pSphere1.vtx[20197:20207] pSphere1.vtx[20233:20246] pSphere1.vtx[20278:20302] pSphere1.vtx[20397:20407] pSphere1.vtx[20433:20446] pSphere1.vtx[20478:20500] pSphere1.vtx[20597:20607] pSphere1.vtx[20633:20646] pSphere1.vtx[20678:20698] pSphere1.vtx[20797:20807] pSphere1.vtx[20833:20846] pSphere1.vtx[20878:20895] pSphere1.vtx[20997:21007] pSphere1.vtx[21033:21046] pSphere1.vtx[21078:21093] pSphere1.vtx[21197:21207] pSphere1.vtx[21233:21246] pSphere1.vtx[21278:21291] pSphere1.vtx[21397:21407] pSphere1.vtx[21433:21446] pSphere1.vtx[21478:21489] pSphere1.vtx[21597:21607] pSphere1.vtx[21633:21646] pSphere1.vtx[21678:21687] pSphere1.vtx[21797:21807] pSphere1.vtx[21833:21846] pSphere1.vtx[21878:21885] pSphere1.vtx[21998:22006] pSphere1.vtx[22033:22046] pSphere1.vtx[22078:22082] pSphere1.vtx[22202:22206] pSphere1.vtx[22233:22246] pSphere1.vtx[22279:22280] pSphere1.vtx[22404:22406] pSphere1.vtx[22433:22446] pSphere1.vtx[22633:22646] pSphere1.vtx[25531:25533] pSphere1.vtx[25557:25559] pSphere1.vtx[25727:25733] pSphere1.vtx[25757:25762] pSphere1.vtx[25923:25933] pSphere1.vtx[25958:25964] pSphere1.vtx[26122:26133] pSphere1.vtx[26158:26167] pSphere1.vtx[26322:26333] pSphere1.vtx[26358:26369] pSphere1.vtx[26522:26533] pSphere1.vtx[26558:26571] pSphere1.vtx[26722:26733] pSphere1.vtx[26758:26773] pSphere1.vtx[26921:26933] pSphere1.vtx[26959:26975] pSphere1.vtx[27121:27133] pSphere1.vtx[27159:27177] pSphere1.vtx[27321:27332] pSphere1.vtx[27359:27379] pSphere1.vtx[27520:27532] pSphere1.vtx[27559:27581] pSphere1.vtx[27720:27732] pSphere1.vtx[27760:27783] pSphere1.vtx[27920:27932] pSphere1.vtx[27960:27985] pSphere1.vtx[28119:28132] pSphere1.vtx[28160:28187] pSphere1.vtx[28319:28332] pSphere1.vtx[28361:28388] pSphere1.vtx[28519:28532] pSphere1.vtx[28561:28590] pSphere1.vtx[28718:28732] pSphere1.vtx[28761:28792] pSphere1.vtx[28918:28931] pSphere1.vtx[28962:28994] pSphere1.vtx[29117:29131] pSphere1.vtx[29162:29196] pSphere1.vtx[29317:29331] pSphere1.vtx[29363:29398] pSphere1.vtx[29400] pSphere1.vtx[29516:29531] pSphere1.vtx[29565:29602] pSphere1.vtx[29715:29731] pSphere1.vtx[29767:29805] pSphere1.vtx[29915:29931] pSphere1.vtx[29969:30007] pSphere1.vtx[30114:30130] pSphere1.vtx[30170:30210] pSphere1.vtx[30313:30330] pSphere1.vtx[30372:30411] pSphere1.vtx[30512:30530] pSphere1.vtx[30574:30610] pSphere1.vtx[30674:30676] pSphere1.vtx[30712:30730] pSphere1.vtx[30775:30810] pSphere1.vtx[30871:30877] pSphere1.vtx[30911:30930] pSphere1.vtx[30977:31009] pSphere1.vtx[31068:31079] pSphere1.vtx[31109:31129] pSphere1.vtx[31178:31208] pSphere1.vtx[31265:31280] pSphere1.vtx[31308:31329] pSphere1.vtx[31380:31407] pSphere1.vtx[31461:31481] pSphere1.vtx[31507:31529] pSphere1.vtx[31581:31606] pSphere1.vtx[31657:31683] pSphere1.vtx[31705:31729] pSphere1.vtx[31783:31805] pSphere1.vtx[31856:31885] pSphere1.vtx[31903:31928] pSphere1.vtx[31985:32004] pSphere1.vtx[32056:32089] pSphere1.vtx[32099:32128] pSphere1.vtx[32186:32203] pSphere1.vtx[32257:32328] pSphere1.vtx[32388:32401] pSphere1.vtx[32457:32527] pSphere1.vtx[32589:32599] pSphere1.vtx[32658:32727] pSphere1.vtx[32791:32799] pSphere1.vtx[32858:32926] pSphere1.vtx[32993:32997] pSphere1.vtx[33058:33126] pSphere1.vtx[33259:33326] pSphere1.vtx[33459:33525] pSphere1.vtx[33660:33724] pSphere1.vtx[33861:33924] pSphere1.vtx[34061:34123] pSphere1.vtx[34262:34323] pSphere1.vtx[34463:34522] pSphere1.vtx[34663:34721] pSphere1.vtx[34864:34920] pSphere1.vtx[35065:35119] pSphere1.vtx[35267:35318] pSphere1.vtx[35468:35516] pSphere1.vtx[35669:35715] pSphere1.vtx[35871:35913] pSphere1.vtx[36073:36111] pSphere1.vtx[36276:36308] pSphere1.vtx[36479:36505] pSphere1.vtx[36685:36699]") ## Get all vtx in set. (We dont care about the order. So set is faster then list.) ## Using cmds and string conversion, as its faster then pymel. vtxSelectionSet = set( [int(x.split(".vtx[")[-1][:-1]) for x in set(cmds.ls(sl=True, o=False, fl = True)) - set(cmds.ls(sl=True, o=True))]) meshName = "pSphere1" ## Start a simple timer to know how long it takes. start = time.time() vertexGroups = getVertexGroups(meshName, vtxSelectionSet) print "Duration: %s seconds" %str(time.time()- start ) for groep, entries in enumerate(vertexGroups): print "Groep : " ,groep print "\tMin : " ,min(entries) print "\tMax : " ,max(entries) |