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.
    macroScript IC_SelectSubDivNet
    category:"IllusionCatalyst Tools"
    buttonText:"SelectSubDivNet"
    tooltip:"IC.SelectSubDivNet"
    (
        on execute do
        (
            if (Filters.Is_EditPoly()) do
            (
                local theNode = selection[1]
                local theBase = theNode.baseObject
    
                local baRefVert = polyOp.getVertSelection theBase
    
                if (baRefVert.numberSet == 1) do
                (
                    local baOldEdgeSel = #{}
                    local baNewEdgeSel = polyOp.getEdgesUsingVert theBase baRefVert
    
                    local baEdgeVerts = #{}
                    local baEdgeFaces = #{}
    
                    local baExtEdges = #{}
                    local baNewGridVerts = #{}
    
                    while (baNewEdgeSel.numberSet != baOldEdgeSel.numberSet) do
                    (
                        if (keyboard.escPressed) do
                            throw "**Esc Pressed**"
    
                        baEdgeVerts = polyOp.getVertsUsingEdge theBase baNewEdgeSel
                        baEdgeFaces = polyOp.getFacesUsingEdge theBase baNewEdgeSel
    
                        baExtEdges = (polyOp.getEdgesUsingVert theBase baEdgeVerts) - (polyOp.getEdgesUsingFace theBase baEdgeFaces)
                        baNewGridVerts = (polyOp.getVertsUsingEdge theBase baExtEdges) - baEdgeVerts
    
                        baOldEdgeSel = baNewEdgeSel
                        baNewEdgeSel += polyOp.getEdgesUsingVert theBase baNewGridVerts
                    )
    
                    polyOp.setEdgeSelection theBase baNewEdgeSel
                    subObjectLevel = 2
                )
            )
        )
    )
    
  • 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.
    macroScript IC_SelectSubDivNet
    category:"IllusionCatalyst Tools"
    buttonText:"SelectSubDivNet"
    tooltip:"IC.SelectSubDivNet"
    (
        function getEPolyEdgesUsingFace theNode theEditPoly baFaces =
        (
            local baEdges = #{}
            local iFaceDeg = 0
    
            for iFace in baFaces do
            (
                iFaceDeg = theEditPoly.getFaceDegree iFace node:theNode
    
                for iSide = 1 to iFaceDeg do
                    baEdges[theEditPoly.getFaceEdge iFace iSide node:theNode] = true
            )
            return baEdges
        )
    
        on execute do
        (
            if (Filters.Is_EPoly()) do
            (
                local theNode = selection[1]
                local theBase = theNode.baseObject
    
                if (getCommandPanelTaskMode() != #modify) do
                    setCommandPanelTaskMode #modify
    
                local theEditObj = modPanel.getCurrentObject()
    
                local baOldEdgeSel = #{}
                local baNewEdgeSel = #{}
    
                local baEdgeVerts = #{}
                local baEdgeFaces = #{}
    
                local baExtEdges = #{}
                local baNewGridVerts = #{}
    
                if (classOf theEditObj == Editable_Poly) then
                (
                    local baRefVert = polyOp.getVertSelection theBase
    
                    if (baRefVert.numberSet == 1) do
                    (
                        baNewEdgeSel = polyOp.getEdgesUsingVert theBase baRefVert
    
                        while (baNewEdgeSel.numberSet != baOldEdgeSel.numberSet) do
                        (
                            if (keyboard.escPressed) do
                                throw "**Esc Pressed**"
    
                            baEdgeVerts = polyOp.getVertsUsingEdge theBase baNewEdgeSel
                            baEdgeFaces = polyOp.getFacesUsingEdge theBase baNewEdgeSel
    
                            baExtEdges = (polyOp.getEdgesUsingVert theBase baEdgeVerts) - (polyOp.getEdgesUsingFace theBase baEdgeFaces)
                            baNewGridVerts = (polyOp.getVertsUsingEdge theBase baExtEdges) - baEdgeVerts
    
                            baOldEdgeSel = baNewEdgeSel
                            baNewEdgeSel += polyOp.getEdgesUsingVert theBase baNewGridVerts
                        )
    
                        polyOp.setEdgeSelection theBase baNewEdgeSel
                        subObjectLevel = 2
                    )
                )
                else if ((classOf theEditObj) == Edit_Poly) then
                (
                    local baRefVert = theEditObj.getSelection #Vertex node:theNode
    
                    if (baRefVert.numberSet == 1) do
                    (
                        theEditObj.getEdgesUsingVert &baNewEdgeSel baRefVert node:theNode
    
                        local baEdgesFromVerts = #{}
                        local baEdgesFromFaces = #{}
                        local baAddEdges = #{}
    
                        while (baNewEdgeSel.numberSet != baOldEdgeSel.numberSet) do
                        (
                            if (keyboard.escPressed) do
                                throw "**Esc Pressed**"
    
                            theEditObj.getVertsUsingEdge &baEdgeVerts baNewEdgeSel
                            theEditObj.getFacesUsingEdge &baEdgeFaces baNewEdgeSel
    
                            theEditObj.getEdgesUsingVert &baEdgesFromVerts baEdgeVerts
                            baEdgesFromFaces = getEPolyEdgesUsingFace theNode theEditObj baEdgeFaces
    
                            baExtEdges = baEdgesFromVerts - baEdgesFromFaces
    
                            theEditObj.getVertsUsingEdge &baNewGridVerts baExtEdges
                            baNewGridVerts -= baEdgeVerts
    
                            baOldEdgeSel = baNewEdgeSel
    
                            theEditObj.getEdgesUsingVert &baAddEdges baNewGridVerts
                            baNewEdgeSel += baAddEdges
                        )
    
                        theEditObj.setSelection #Edge baNewEdgeSel node:theNode
                        theEditObj.setEPolySelLevel #Edge
                    )
                )
            )
        )
    )
    
  • 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.
    macroScript IC_SelectSubDivNet
    category:"IllusionCatalyst Tools"
    buttonText:"SelectSubDivNet"
    tooltip:"IC.SelectSubDivNet"
    (
        rollout rolSelectSubDivNet "Select Sub Div Net"
        (
            label lb_1 "Analysing Geometry..." align:#center offset:[0,0]
            progressBar pbStatus width:100 height:9 color:[0,197,0] align:#center offset:[0,0]
            label lb_2 "Press ESC to cancel" align:#center offset:[0,0]
        )
    
        function getEPolyEdgesUsingFace theNode theEditPoly baFaces =
        (
            local baEdges = #{}
            local iFaceDeg = 0
    
            for iFace in baFaces do
            (
                iFaceDeg = theEditPoly.getFaceDegree iFace node:theNode
    
                for iSide = 1 to iFaceDeg do
                    baEdges[theEditPoly.getFaceEdge iFace iSide node:theNode] = true
            )
            return baEdges
        )
    
        on execute do
        (
            if (Filters.Is_EPoly()) do
            (
                createDialog rolSelectSubDivNet 120 57 style:#(#style_toolwindow, #style_sysmenu, #style_resizing)
    
                local theNode = selection[1]
                local theBase = theNode.baseObject
    
                if (getCommandPanelTaskMode() != #modify) do
                    setCommandPanelTaskMode #modify
    
                local theEditObj = modPanel.getCurrentObject()
    
                local baOldEdgeSel = #{}
                local baNewEdgeSel = #{}
    
                local baEdgeVerts = #{}
                local baEdgeFaces = #{}
    
                local baExtEdges = #{}
                local baNewGridVerts = #{}
    
                local baRefVert = #{}
    
                local iNumTestEdges = 0
                local bTest = true
    
                if (classOf theEditObj == Editable_Poly) then
                (
                    iNumTestEdges = ( (polyOp.getNumEdges theBase) - ((polyOp.getOpenEdges theBase).numberSet) ) * .5
    
                    baRefVert = polyOp.getVertSelection theBase
    
                    if (baRefVert.numberSet == 1) do
                    (
                        baNewEdgeSel = polyOp.getEdgesUsingVert theBase baRefVert
    
                        while (baNewEdgeSel.numberSet != baOldEdgeSel.numberSet) do
                        (
                            if (keyboard.escPressed) do
                            (
                                bTest = false
                                exit
                            )
    
                            rolSelectSubDivNet.pbStatus.value = ((baOldEdgeSel.numberSet * 100 / iNumTestEdges) as Integer)
    
                            baEdgeVerts = polyOp.getVertsUsingEdge theBase (baNewEdgeSel - baOldEdgeSel)
                            baEdgeFaces = polyOp.getFacesUsingEdge theBase (baNewEdgeSel - baOldEdgeSel)
    
                            baExtEdges = (polyOp.getEdgesUsingVert theBase baEdgeVerts) - (polyOp.getEdgesUsingFace theBase baEdgeFaces) - baOldEdgeSel
                            baNewGridVerts = (polyOp.getVertsUsingEdge theBase baExtEdges) - baEdgeVerts
    
                            baOldEdgeSel = baNewEdgeSel
                            baNewEdgeSel += polyOp.getEdgesUsingVert theBase baNewGridVerts
                        )
    
                        if (bTest == true) do
                        (
                            polyOp.setEdgeSelection theBase baNewEdgeSel
                            subObjectLevel = 2
                        )
                    )
                )
                else if ((classOf theEditObj) == Edit_Poly) then
                (
                    iNumTestEdges = ( (theEditObj.getNumEdges node:theNode) - ((theEditObj.getOpenEdges node:theNode).numberSet) ) * .5
    
                    baRefVert = theEditObj.getSelection #Vertex node:theNode
    
                    if (baRefVert.numberSet == 1) do
                    (
                        theEditObj.getEdgesUsingVert &baNewEdgeSel baRefVert node:theNode
    
                        local baEdgesFromVerts = #{}
                        local baEdgesFromFaces = #{}
                        local baAddEdges = #{}
    
                        while (baNewEdgeSel.numberSet != baOldEdgeSel.numberSet) do
                        (
                            if (keyboard.escPressed) do
                            (
                                bTest = false
                                exit
                            )
    
                            rolSelectSubDivNet.pbStatus.value = ((baOldEdgeSel.numberSet * 100 / (iNumTestEdges as Float)) as Integer)
    
                            theEditObj.getVertsUsingEdge &baEdgeVerts (baNewEdgeSel - baOldEdgeSel)
                            theEditObj.getFacesUsingEdge &baEdgeFaces (baNewEdgeSel - baOldEdgeSel)
    
                            theEditObj.getEdgesUsingVert &baEdgesFromVerts baEdgeVerts
                            baEdgesFromFaces = getEPolyEdgesUsingFace theNode theEditObj baEdgeFaces
    
                            baExtEdges = baEdgesFromVerts - baEdgesFromFaces - baOldEdgeSel
    
                            theEditObj.getVertsUsingEdge &baNewGridVerts baExtEdges
                            baNewGridVerts -= baEdgeVerts
    
                            baOldEdgeSel = baNewEdgeSel
    
                            theEditObj.getEdgesUsingVert &baAddEdges baNewGridVerts
                            baNewEdgeSel += baAddEdges
                        )
    
                        if (bTest == true) do
                        (
                            baNewEdgeSel.count = theEditObj.getNumEdges node:theNode
                            theEditObj.setSelection #Edge baNewEdgeSel node:theNode
                            theEditObj.setEPolySelLevel #Edge
                        )
                    )
                )
    
                try ( destroyDialog rolSelectSubDivNet ) catch ()
            )
    
            gc light:true
        )
    )
    
  • 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.