Home Technical Talk

select every other edge on collapsed subdivided mesh - 3dsmax

grand marshal polycounter
Offline / Send Message
pior grand marshal polycounter
Hi all!

In a few occasions lately I ve been in the need of going back to a subdiv mesh and manually remove the edges generated by subdivision, in order to get the base cage back.

It's not a very hard thing to do really - just a matter of selecting every other edge loops, and removing them. The result is pretty much what 'reconstruct subdiv' does in Zbrush.

I know that in theory one should always keep the base mesh somewhere, but even when being very careful there is always a time when this comes up. Basically, manually selecting things like here :

everyotheredge.jpg

My question is, do you guys know of some maxscript doing just that : user selects an edgeloop, and the script selects every other parallel edge loop from there ? It would be even better if one could select 2 edges forming an L branch somewhere on a mesh, and get all the subdivision-generated edges from that. But thats just icing on the cake !

Ideas?

As usual, this is a valid question for Max, Maya or any other app...

Replies

  • Mark Dygert
    Polyboost/Graphite Modeling Tools has Dot Loop and Dot Ring feature that selects every other edge, but its very literal and not as robust as what you're hunting for still might be a good place to start.
  • SyncViewS
    Offline / Send Message
    SyncViewS polycounter lvl 13
    Hi Pior, here is a script to do the job. Select a single vertex, then run it and it will automatically select the whole subdivision edge net staring from that vertex. The script currently works for Editable Poly only. Let me know if you need it for Edit Poly Modifiers too. It would be a little more complicate and slower because Edit Poly Modifier doesn't have a command need and I'd have to figure out a workaround. The code is not super tested, but should work well. Please let me know about any issue.
    1. macroScript IC_SelectSubDivNet
    2. category:"IllusionCatalyst Tools"
    3. buttonText:"SelectSubDivNet"
    4. tooltip:"IC.SelectSubDivNet"
    5. (
    6. on execute do
    7. (
    8. if (Filters.Is_EditPoly()) do
    9. (
    10. local theNode = selection[1]
    11. local theBase = theNode.baseObject
    12.  
    13. local baRefVert = polyOp.getVertSelection theBase
    14.  
    15. if (baRefVert.numberSet == 1) do
    16. (
    17. local baOldEdgeSel = #{}
    18. local baNewEdgeSel = polyOp.getEdgesUsingVert theBase baRefVert
    19.  
    20. local baEdgeVerts = #{}
    21. local baEdgeFaces = #{}
    22.  
    23. local baExtEdges = #{}
    24. local baNewGridVerts = #{}
    25.  
    26. while (baNewEdgeSel.numberSet != baOldEdgeSel.numberSet) do
    27. (
    28. if (keyboard.escPressed) do
    29. throw "**Esc Pressed**"
    30.  
    31. baEdgeVerts = polyOp.getVertsUsingEdge theBase baNewEdgeSel
    32. baEdgeFaces = polyOp.getFacesUsingEdge theBase baNewEdgeSel
    33.  
    34. baExtEdges = (polyOp.getEdgesUsingVert theBase baEdgeVerts) - (polyOp.getEdgesUsingFace theBase baEdgeFaces)
    35. baNewGridVerts = (polyOp.getVertsUsingEdge theBase baExtEdges) - baEdgeVerts
    36.  
    37. baOldEdgeSel = baNewEdgeSel
    38. baNewEdgeSel += polyOp.getEdgesUsingVert theBase baNewGridVerts
    39. )
    40.  
    41. polyOp.setEdgeSelection theBase baNewEdgeSel
    42. subObjectLevel = 2
    43. )
    44. )
    45. )
    46. )
  • PolyHertz
    Offline / Send Message
    PolyHertz polycount lvl 666
    SyncViews cures cancer

    *Head explodes*

    So awesome. Thanks Sync! :)
  • MoP
    Offline / Send Message
    MoP polycounter lvl 18
    perna: Did you read his question wrongly? What you describe sounds totally weird... it's like you're adding MeshSmooth in order to subdivide it and then get the inverse selection of the edges... that doesn't get you back to your original "control cage" at all!

    SyncViewS' script works perfectly though. So simple, too!

    I might look into doing this script for Maya too, I needed it recently. Reconstruct Subdiv for any mesh!
  • PolyHertz
    Offline / Send Message
    PolyHertz polycount lvl 666
    Perna: This is more in the case that you lose the lowpoly for one reason or another and need to get it back via removing the added edges. Of course, even doing that you'll lose volume as the original wires have been relaxed to flow with the new ones better. But either way, think you've got the wrong idea why this is useful.

    MoP: Do it! Every program should have something like this :)
  • undoz
    SyncViewS: that's brilliant! I was looking for something like this for ages.
  • MoP
    Offline / Send Message
    MoP polycounter lvl 18
    I'd love to see a model where you used that... what do you do it for? I honestly can't think of a use but clearly you have some! Do tell! :)
  • pior
    Offline / Send Message
    pior grand marshal polycounter
    Lol at per haha.

    Try this : apply the meshsmooth alright. unselect all the edges. Close Max. Open Max. Open your scene. Go to edge mode. Huh what do you do now ?

    Mop : Pers technique can be used to make tire treads, bumpy stuff on gun handles and so on. Once you got that selection of edges bases on the original cage flow you can make very cool stuff by just moving it along the normals.
  • MoP
    Offline / Send Message
    MoP polycounter lvl 18
    Oh right, I see. I think for that stuff I usually script it ;o
    Or more likely someone else models it :D
  • SyncViewS
    Offline / Send Message
    SyncViewS polycounter lvl 13
    Hi guys, I'm glad you liked it. Here is the next step working for Editable Poly and Edit Poly Modifiers. It is considerably slower on modifiers, but I guess it is better than nothing. Please report any issue. Thanks.
    1. macroScript IC_SelectSubDivNet
    2. category:"IllusionCatalyst Tools"
    3. buttonText:"SelectSubDivNet"
    4. tooltip:"IC.SelectSubDivNet"
    5. (
    6. function getEPolyEdgesUsingFace theNode theEditPoly baFaces =
    7. (
    8. local baEdges = #{}
    9. local iFaceDeg = 0
    10.  
    11. for iFace in baFaces do
    12. (
    13. iFaceDeg = theEditPoly.getFaceDegree iFace node:theNode
    14.  
    15. for iSide = 1 to iFaceDeg do
    16. baEdges[theEditPoly.getFaceEdge iFace iSide node:theNode] = true
    17. )
    18. return baEdges
    19. )
    20.  
    21. on execute do
    22. (
    23. if (Filters.Is_EPoly()) do
    24. (
    25. local theNode = selection[1]
    26. local theBase = theNode.baseObject
    27.  
    28. if (getCommandPanelTaskMode() != #modify) do
    29. setCommandPanelTaskMode #modify
    30.  
    31. local theEditObj = modPanel.getCurrentObject()
    32.  
    33. local baOldEdgeSel = #{}
    34. local baNewEdgeSel = #{}
    35.  
    36. local baEdgeVerts = #{}
    37. local baEdgeFaces = #{}
    38.  
    39. local baExtEdges = #{}
    40. local baNewGridVerts = #{}
    41.  
    42. if (classOf theEditObj == Editable_Poly) then
    43. (
    44. local baRefVert = polyOp.getVertSelection theBase
    45.  
    46. if (baRefVert.numberSet == 1) do
    47. (
    48. baNewEdgeSel = polyOp.getEdgesUsingVert theBase baRefVert
    49.  
    50. while (baNewEdgeSel.numberSet != baOldEdgeSel.numberSet) do
    51. (
    52. if (keyboard.escPressed) do
    53. throw "**Esc Pressed**"
    54.  
    55. baEdgeVerts = polyOp.getVertsUsingEdge theBase baNewEdgeSel
    56. baEdgeFaces = polyOp.getFacesUsingEdge theBase baNewEdgeSel
    57.  
    58. baExtEdges = (polyOp.getEdgesUsingVert theBase baEdgeVerts) - (polyOp.getEdgesUsingFace theBase baEdgeFaces)
    59. baNewGridVerts = (polyOp.getVertsUsingEdge theBase baExtEdges) - baEdgeVerts
    60.  
    61. baOldEdgeSel = baNewEdgeSel
    62. baNewEdgeSel += polyOp.getEdgesUsingVert theBase baNewGridVerts
    63. )
    64.  
    65. polyOp.setEdgeSelection theBase baNewEdgeSel
    66. subObjectLevel = 2
    67. )
    68. )
    69. else if ((classOf theEditObj) == Edit_Poly) then
    70. (
    71. local baRefVert = theEditObj.getSelection #Vertex node:theNode
    72.  
    73. if (baRefVert.numberSet == 1) do
    74. (
    75. theEditObj.getEdgesUsingVert &baNewEdgeSel baRefVert node:theNode
    76.  
    77. local baEdgesFromVerts = #{}
    78. local baEdgesFromFaces = #{}
    79. local baAddEdges = #{}
    80.  
    81. while (baNewEdgeSel.numberSet != baOldEdgeSel.numberSet) do
    82. (
    83. if (keyboard.escPressed) do
    84. throw "**Esc Pressed**"
    85.  
    86. theEditObj.getVertsUsingEdge &baEdgeVerts baNewEdgeSel
    87. theEditObj.getFacesUsingEdge &baEdgeFaces baNewEdgeSel
    88.  
    89. theEditObj.getEdgesUsingVert &baEdgesFromVerts baEdgeVerts
    90. baEdgesFromFaces = getEPolyEdgesUsingFace theNode theEditObj baEdgeFaces
    91.  
    92. baExtEdges = baEdgesFromVerts - baEdgesFromFaces
    93.  
    94. theEditObj.getVertsUsingEdge &baNewGridVerts baExtEdges
    95. baNewGridVerts -= baEdgeVerts
    96.  
    97. baOldEdgeSel = baNewEdgeSel
    98.  
    99. theEditObj.getEdgesUsingVert &baAddEdges baNewGridVerts
    100. baNewEdgeSel += baAddEdges
    101. )
    102.  
    103. theEditObj.setSelection #Edge baNewEdgeSel node:theNode
    104. theEditObj.setEPolySelLevel #Edge
    105. )
    106. )
    107. )
    108. )
    109. )
  • MoP
    Offline / Send Message
    MoP polycounter lvl 18
    I've got an initial version of this working in Maya - one of the nice things about MEL is that I don't have to worry about whether something is an Editable Poly or an Editable Mesh, or if it has Modifiers on it... it's all just mesh data! :)

    I'll add a progress bar since it gets a bit slow on larger meshes, then release it tomorrow after more testing. Should be pretty handy!

    Ideally I'd like to do a proper "reconstruct subdiv" like ZBrush has but I'm not entirely sure where to start. I guess you have to start by dividing the total number of polys in the mesh by 4 to check that it's actually been subdivided at all?
  • pior
    Offline / Send Message
    pior grand marshal polycounter
    Yeah Per the more I think of it the more I am wondering why only Zbrush has it ?

    And about the original post : I really mean on a collapsed mesh, of course it's not a problem at all when the source cage is still available ... that's why your 'tadaaaa here's how to do it' post made me laugh. It's cool tho, had a long day!
  • SyncViewS
    Offline / Send Message
    SyncViewS polycounter lvl 13
    Hi guys, here is the latest update. The code is a little more optimized and quite faster with Edit Poly Modifiers. I added a rollout with a progressbar as suggested by MoP to keep the user informed about the current calculation status. Please report any issue. Thank you.
    1. macroScript IC_SelectSubDivNet
    2. category:"IllusionCatalyst Tools"
    3. buttonText:"SelectSubDivNet"
    4. tooltip:"IC.SelectSubDivNet"
    5. (
    6. rollout rolSelectSubDivNet "Select Sub Div Net"
    7. (
    8. label lb_1 "Analysing Geometry..." align:#center offset:[0,0]
    9. progressBar pbStatus width:100 height:9 color:[0,197,0] align:#center offset:[0,0]
    10. label lb_2 "Press ESC to cancel" align:#center offset:[0,0]
    11. )
    12.  
    13. function getEPolyEdgesUsingFace theNode theEditPoly baFaces =
    14. (
    15. local baEdges = #{}
    16. local iFaceDeg = 0
    17.  
    18. for iFace in baFaces do
    19. (
    20. iFaceDeg = theEditPoly.getFaceDegree iFace node:theNode
    21.  
    22. for iSide = 1 to iFaceDeg do
    23. baEdges[theEditPoly.getFaceEdge iFace iSide node:theNode] = true
    24. )
    25. return baEdges
    26. )
    27.  
    28. on execute do
    29. (
    30. if (Filters.Is_EPoly()) do
    31. (
    32. createDialog rolSelectSubDivNet 120 57 style:#(#style_toolwindow, #style_sysmenu, #style_resizing)
    33.  
    34. local theNode = selection[1]
    35. local theBase = theNode.baseObject
    36.  
    37. if (getCommandPanelTaskMode() != #modify) do
    38. setCommandPanelTaskMode #modify
    39.  
    40. local theEditObj = modPanel.getCurrentObject()
    41.  
    42. local baOldEdgeSel = #{}
    43. local baNewEdgeSel = #{}
    44.  
    45. local baEdgeVerts = #{}
    46. local baEdgeFaces = #{}
    47.  
    48. local baExtEdges = #{}
    49. local baNewGridVerts = #{}
    50.  
    51. local baRefVert = #{}
    52.  
    53. local iNumTestEdges = 0
    54. local bTest = true
    55.  
    56. if (classOf theEditObj == Editable_Poly) then
    57. (
    58. iNumTestEdges = ( (polyOp.getNumEdges theBase) - ((polyOp.getOpenEdges theBase).numberSet) ) * .5
    59.  
    60. baRefVert = polyOp.getVertSelection theBase
    61.  
    62. if (baRefVert.numberSet == 1) do
    63. (
    64. baNewEdgeSel = polyOp.getEdgesUsingVert theBase baRefVert
    65.  
    66. while (baNewEdgeSel.numberSet != baOldEdgeSel.numberSet) do
    67. (
    68. if (keyboard.escPressed) do
    69. (
    70. bTest = false
    71. exit
    72. )
    73.  
    74. rolSelectSubDivNet.pbStatus.value = ((baOldEdgeSel.numberSet * 100 / iNumTestEdges) as Integer)
    75.  
    76. baEdgeVerts = polyOp.getVertsUsingEdge theBase (baNewEdgeSel - baOldEdgeSel)
    77. baEdgeFaces = polyOp.getFacesUsingEdge theBase (baNewEdgeSel - baOldEdgeSel)
    78.  
    79. baExtEdges = (polyOp.getEdgesUsingVert theBase baEdgeVerts) - (polyOp.getEdgesUsingFace theBase baEdgeFaces) - baOldEdgeSel
    80. baNewGridVerts = (polyOp.getVertsUsingEdge theBase baExtEdges) - baEdgeVerts
    81.  
    82. baOldEdgeSel = baNewEdgeSel
    83. baNewEdgeSel += polyOp.getEdgesUsingVert theBase baNewGridVerts
    84. )
    85.  
    86. if (bTest == true) do
    87. (
    88. polyOp.setEdgeSelection theBase baNewEdgeSel
    89. subObjectLevel = 2
    90. )
    91. )
    92. )
    93. else if ((classOf theEditObj) == Edit_Poly) then
    94. (
    95. iNumTestEdges = ( (theEditObj.getNumEdges node:theNode) - ((theEditObj.getOpenEdges node:theNode).numberSet) ) * .5
    96.  
    97. baRefVert = theEditObj.getSelection #Vertex node:theNode
    98.  
    99. if (baRefVert.numberSet == 1) do
    100. (
    101. theEditObj.getEdgesUsingVert &baNewEdgeSel baRefVert node:theNode
    102.  
    103. local baEdgesFromVerts = #{}
    104. local baEdgesFromFaces = #{}
    105. local baAddEdges = #{}
    106.  
    107. while (baNewEdgeSel.numberSet != baOldEdgeSel.numberSet) do
    108. (
    109. if (keyboard.escPressed) do
    110. (
    111. bTest = false
    112. exit
    113. )
    114.  
    115. rolSelectSubDivNet.pbStatus.value = ((baOldEdgeSel.numberSet * 100 / (iNumTestEdges as Float)) as Integer)
    116.  
    117. theEditObj.getVertsUsingEdge &baEdgeVerts (baNewEdgeSel - baOldEdgeSel)
    118. theEditObj.getFacesUsingEdge &baEdgeFaces (baNewEdgeSel - baOldEdgeSel)
    119.  
    120. theEditObj.getEdgesUsingVert &baEdgesFromVerts baEdgeVerts
    121. baEdgesFromFaces = getEPolyEdgesUsingFace theNode theEditObj baEdgeFaces
    122.  
    123. baExtEdges = baEdgesFromVerts - baEdgesFromFaces - baOldEdgeSel
    124.  
    125. theEditObj.getVertsUsingEdge &baNewGridVerts baExtEdges
    126. baNewGridVerts -= baEdgeVerts
    127.  
    128. baOldEdgeSel = baNewEdgeSel
    129.  
    130. theEditObj.getEdgesUsingVert &baAddEdges baNewGridVerts
    131. baNewEdgeSel += baAddEdges
    132. )
    133.  
    134. if (bTest == true) do
    135. (
    136. baNewEdgeSel.count = theEditObj.getNumEdges node:theNode
    137. theEditObj.setSelection #Edge baNewEdgeSel node:theNode
    138. theEditObj.setEPolySelLevel #Edge
    139. )
    140. )
    141. )
    142.  
    143. try ( destroyDialog rolSelectSubDivNet ) catch ()
    144. )
    145.  
    146. gc light:true
    147. )
    148. )
  • mLichy
    Sweet, thanks again Sync. I was looking over the first post, and understand more so now how I can get my ring function working that we talked about :).
  • SyncViewS
    Offline / Send Message
    SyncViewS polycounter lvl 13
    Thank you Matt, you nailed it, the principle in this case is similar to the function you want to write. From a starting condition, create an algorithm that in one cycle gets the neighbour elements you need by walking through elements in relation to each others, then iterate it until meet the ending condition. Let me know if you need explanation on this script.
  • pior
    Offline / Send Message
    pior grand marshal polycounter
    Hi! Bit of an old thread but I just wanted to jump back in and let you know that the script is working perfectly. I just managed to get back a lost basemesh that had been subdivided two times and collapsed. Fantastic!! That's kinda huge!!
  • MoP
    Offline / Send Message
    MoP polycounter lvl 18
    FYI I've got a version of this working in Maya too, but it's super-slow on dense meshes. Gonna see what I can do about that but it's not really useable at the moment :/
  • SyncViewS
    Offline / Send Message
    SyncViewS polycounter lvl 13
    Thanks pior! I'm glad it helped you, even if it isn't a real topology reconstructor. If you ever need a scripter in Bliz, remember me :D
Sign In or Register to comment.