Home Technical Talk

3ds Max vertex re-ordering... script?

Anyone know if there is a way to re-order a model's vertices in 3ds Max 2015 (or any version, really) so they are numbered in order from bottom to top on the up axis?

I am making foliage for iOS/Unity, and alpha blend is required (PVR GPU renders alpha test much slower than alpha blend). But the transparency sorting is terrible.

Good thing is, my foliage is being used in an iso 3d game, so it's mostly viewed from above. If I order my vertices from bottom to top, I get reasonably good sorting. Bad thing is, 3ds Max keeps messing with my vertex order. I can detach and re-attach, but detaching kills my custom vertex normals.

Is there any tool that will help me re-order the verts along a particular axis (up axis would be great)?

Replies

  • echofourpapa
    Offline / Send Message
    echofourpapa polycounter lvl 4
    Not that I'm aware of. Also, wouldn't Unity reorder the verts on import anyway?
  • Eric Chadwick
    Nope. At least when pre-exporting my FBX files, it seems to preserve the order I set.
  • Klunk
    Offline / Send Message
    Klunk ngon master
    I think any re ordering script would probably trash any custom normal settings you had on the mesh. As for detaching attaching it may be worth looking around for a normals->mapchannel and mapchannel->normals script. You store the normals in an arbitary map channel before you edit safe in the knowledge they are less likely to get trashed by the edit then you can restore them afterwards. There was a thread on CGSoc on maintaining normals when tesselating using this kind of approach.
  • Noors
    Offline / Send Message
    Noors greentooth
    detach attach with stored normals in a channel, or use normal thief with the original model.
  • Cremuss
    Offline / Send Message
    Cremuss polycounter lvl 12
    I don't know about 3dsmax, but if you are done with the model, you could import an obj into Blender and do that final step there.

    It's pretty straight forward, import, go to edit mode, press space bar to search a fonction, type "sort mesh elements" and there you can sort vertices or faces (depending on your selection) along x/y/z/view/camera, etc...

    Then, in object mode again, you could for instance add a Normal Edit modifier and set it to radial or directionnal to quickly re-customize your normals.

    Export fbx and it's done.
  • monster
    Offline / Send Message
    monster polycounter
    Cremuss wrote: »
    I don't know about 3dsmax, but if you are done with the model, you could import an obj into Blender and do that final step there.

    It's pretty straight forward, import, go to edit mode, press space bar to search a fonction, type "sort mesh elements" and there you can sort vertices or faces (depending on your selection) along x/y/z/view/camera, etc...

    Then, in object mode again, you could for instance add a Normal Edit modifier and set it to radial or directionnal to quickly re-customize your normals.

    Export fbx and it's done.

    Great info man!
  • Eric Chadwick
    Ooh that sounds nice. I wonder if Blender keeps vertex color intact? Fbx supports it but Obj does not (well, except for custom Obj tweaks, like Zbrush does).
  • Cremuss
    Offline / Send Message
    Cremuss polycounter lvl 12
    Vertex colors are intact after exporting a test cube in fbx from Blender and importing it again so I guess that should work just fine.

    Obj in the other hand does break vertex color.

    Be careful with fbx though, it has to be exported in binary mode from your app. Blender cannot open ascii fbx yet.
  • throttlekitty
    I'm sure this works in Max, I've done it in Maya for hair: Separate your mesh into pieces, then combine them back in order. -Or select everything in order then do a combine. But this can be a huge PITA depending on complexity/overlap/viewing angle.

    New verts will append either at the bottom or top of the index. In Maya it's the bottom. Always meant to try that function in Blender, looks handy. Just for funsies, you can also get hardcore picky and use retop tools to manually work in the order you need. (not recommended, but you can get nice blends between interpenetrating faces if you have the time. or even for two quads that look like a plus sign from the top)

    I'm 99% sure that Blender maintains vert index with obj, check your in/out options; I know many who use it for doing morphs alongside other apps.

    woah ninja edit: I missed the part where you mentioned detaching.
  • Eric Chadwick
    Well, somehow I solved it, not sure what was causing the re-ordering. Edited a couple normals and re-exported, bingo it was fixed. Weird. Thanks for all the ideas, I'll have to check out Blender at some point.
  • claydough
    Offline / Send Message
    claydough polycounter lvl 10
    Anyone know if there is a way to re-order a model's vertices in 3ds Max 2015 (or any version, really) so they are numbered in order from bottom to top on the up axis?

    I am making foliage for iOS/Unity, and alpha blend is required (PVR GPU renders alpha test much slower than alpha blend). But the transparency sorting is terrible.

    Good thing is, my foliage is being used in an iso 3d game, so it's mostly viewed from above. If I order my vertices from bottom to top, I get reasonably good sorting. Bad thing is, 3ds Max keeps messing with my vertex order. I can detach and re-attach, but detaching kills my custom vertex normals.

    Is there any tool that will help me re-order the verts along a particular axis (up axis would be great)?

    no one tells me anything! this sounds neat. ( that reordering vrts along an axis may facilitate accurate sorting behavior )

    Could u clear up confusion on my part?

    Is the height in "up" independent of the faces the vrt is a member of?
    Is the vrt order done face by face or a whole object?
    If a whole object...
    Does it matter if vrts are coincidental?

    Can this be done in Maya?
  • claydough
    Offline / Send Message
    claydough polycounter lvl 10
    I mean Automated in Maya instead of aforementioned manual attaching n detaching.
  • Eric Chadwick
    Height includes the faces, because the vertices define the faces, there are no free-floating vertices.

    Order is whole object. There is another set of vertices which have a different material assignment with no transparency (tree trunk) but I could strip those out beforehand.

    Some of the vertices are duplicated in the same location, but that was only because I was detaching and reattaching clumps of triangles and not welding them afterwards. Those could be welded if needed.

    Maya is fine! This could be a very handy tool for people. Especially if alpha testing is deprecated over time, for alpha blending. But it's certainly useful now given how iOS gpu works.
  • claydough
    Offline / Send Message
    claydough polycounter lvl 10
    I meant coincidental in the up axis. For instance, quadrilateral loops in a CUBE( in which case how would u designate the vrt order for those vrts that are in the SAME height position? )
    Wouldn't those vrts that couldn't be differentiated by height affect sorting benefit then?
  • Eric Chadwick
    Not really, they could be randomly assigned an order in that case. You wouldn't be able to tell if you are looking from above, unless the faces were interpenetrating each other, in which case you have other troubles.

    Maybe examine all the vertices connected to the same triangle, and reorder the coincident vertices based on the middles of the triangles?
  • Klunk
    Offline / Send Message
    Klunk ngon master
    something like this (for editable mesh) though it doesn't protect any custom normals (though adding the map channel solution isn't too tricky). It's odd that everything other than the vert order remains the same. If I(we) had a production use for this I would implement it as a modifier with a simple direction gizmo. The script makes a duplicate object to protect the original it's an easy fix to make it sort the original object.
    (
        fn compareInt i1 i2 values: = values[i1] - values[i2];    
            
        fn comparePoint3_Z i1 i2 values: =
        (
            v1 = values[i1];
            v2 = values[i2];
            if v1.z > v2.z then 1 else if v1.z < v2.z then -1 else 0;
        )    
    
    -- sorts mesh verts along the z axis of sorttm    
        
        fn sortMeshVerts mObj sorttm = 
        (
            sorttm.translation = [0,0,0]; -- zero out the tranlation
            local isorttm = (inverse sorttm);
    
    -- collect the vert positions        
            
            local verts = for v = 1 to mObj.numverts collect getvert mObj v * isorttm;
            local indices  = #{1..mObj.numverts} as array;    
            local vertorder = #{1..mObj.numverts} as array;
            
    -- sort the new indices        
            
            qsort vertorder comparePoint3_Z values:verts;
            qsort indices compareInt values:vertorder;  -- "reverse" sort to get the new indexing
            
    -- sort the actual verts restore them     
            
            verts = for v = 1 to verts.count collect verts[vertorder[v]] *= sorttm; 
            
            meshop.setvert mObj #all verts; -- change the verts and face indexing    
            for f = 1 to mObj.numfaces do -- set the faces to new order
            (
                face = getface mObj f;
                setface mObj f [indices[face.x],indices[face.y],indices[face.z]]
            )
            mObj; -- return the sorted object
        )
    
        sortedObj = SortMeshVerts (copy $) $Point01.transform;
    )
    
    edited for slicker method that protects custom normal :)
    another performance edit one too many sorts !
    last edit move the clone out of the function
  • Noors
    Offline / Send Message
    Noors greentooth
    Cool !
    You made 2 lil mistakes tho
    verts = for v = 1 to mObj.numverts collect getvert mObj v * sorttm
    qsort vertorder comparePoint3_Z valArray:verts
    
    Also Point01 isn't defined.
  • Klunk
    Offline / Send Message
    Klunk ngon master
    cut and paste fails I'm afraid, the isortm give the correct results here.
    (
        fn randomPtOnCone theta  = 
        (
            h = cos theta;
            phi =  360.0 * random 0.0 1.0;
            z = h + ( 1 - h ) * random 0.0 1.0; 
            sinT = sqrt (1 - z * z);
            [sinT * cos phi, sinT * sin phi, z];
        )
         
        fn compareInt i1 i2 values: = values[i1] - values[i2];    
            
        fn comparePoint3_Z i1 i2 values: =
        (
            v1 = values[i1];
            v2 = values[i2];
            if v1.z > v2.z then 1 else if v1.z < v2.z then -1 else 0;
        )    
    
    -- sorts mesh verts along the z axis of sorttm    
        
        fn sortMeshVerts mObj sorttm = 
        (
            sorttm.translation = [0,0,0]; -- zero out the tranlation
            local isorttm = inverse sorttm;
    
    -- collect the vert positions        
            
            local verts = for v = 1 to mObj.numverts collect getvert mObj v * isorttm;
            local indices  = #{1..mObj.numverts} as array;    
            local vertorder = #{1..mObj.numverts} as array;
            
    -- sort the new indices        
            
            qsort vertorder comparePoint3_Z values:verts;
            qsort indices compareInt values:vertorder;  -- "reverse" sort to get the new indexing
            
    -- sort the actual verts restore them     
            
            verts = for v = 1 to verts.count collect verts[vertorder[v]] *= sorttm; 
            
    -- change the verts and face indexing        
            
            meshop.setvert mObj #all verts;     
            for f = 1 to mObj.numfaces do -- set the faces to new order
            (
                face = getface mObj f;
                setface mObj f [indices[face.x],indices[face.y],indices[face.z]]
            )
            mObj; -- return the sorted object
        )    
    
        delete objects;
        sph = Sphere radius:25.0 smooth:on segs:32;
        convertToMesh sph;
        sph.selectedverts = #{1};        
        pnt = point  dir:((randomPtOnCone 180) * (matrixfromNormal [0,0,1])) axistripod:on cross:on size:60 wirecolor:(color 198 224 87);  
        t = timestamp();
        sortedObj = sortMeshVerts (copy sph) pnt.transform;
        format "%\n" (timestamp() - t);
        select sortedObj;
        max modify mode;
        subobjectLevel = 1;
    )
    
  • Noors
    Offline / Send Message
    Noors greentooth
    oh my bad for this one, i deleted "local" to test line by line, and it resulted with sorttm = (inverse sorttm) instead of isorttm, hence i thought it was a typo.
  • Eric Chadwick
    I guess I need to wrap this in a proper MCR somehow? I get this error when I evaluate:

    -- Error occurred in anonymous codeblock; filename: ; position: 1464; line: 42
    -- Frame:
    -- comparePoint3_Z: comparePoint3_Z()
    -- sortedObj: undefined
    -- compareInt: compareInt()
    -- sortMeshVerts: sortMeshVerts()
    -- Unknown property: "transform" in undefined

    And the highlight is here:
    sortedObj = SortMeshVerts (copy $) $Point01.transform;
  • Klunk
    Offline / Send Message
    Klunk ngon master
    $point01 is any ol' point helper in the scene used to define the direction of the sort the second script generates the target mesh and a point with random direction as proof of concept.

    you could replace it with
    sortedObj = sortMeshVerts (copy $) (MatrixFromNormal [0,0,1]);
    
    to give a vertical sort
  • Klunk
    Offline / Send Message
    Klunk ngon master
    simplest of macroscripts to sort verts in world Z, will clone all meshes in the current selection and sort their verts.
    macroScript SortVertsZ
                category:     "polycount"
                buttontext:    "SortVertsZ"
                Icon:        #("TrackBar",2)
                tooltip:    "SortVertsZ"
    (
        fn compareInt i1 i2 values: = values[i1] - values[i2];    
            
        fn comparePoint3_Z i1 i2 values: =
        (
            v1 = values[i1];
            v2 = values[i2];
            if v1.z > v2.z then 1 else if v1.z < v2.z then -1 else 0;
        )    
    
    -- sorts mesh verts along the z axis of sorttm    
        
        fn sortMeshVerts mObj sorttm = 
        (
            sorttm.translation = [0,0,0]; -- zero out the tranlation
            local isorttm = inverse sorttm;
    
    -- collect the vert positions        
            
            local verts = for v = 1 to mObj.numverts collect getvert mObj v * isorttm;
            local indices  = #{1..mObj.numverts} as array;    
            local vertorder = #{1..mObj.numverts} as array;
            
    -- sort the new indices        
            
            qsort vertorder comparePoint3_Z values:verts;
            qsort indices compareInt values:vertorder;  -- "reverse" sort to get the new indexing
            
    -- sort the actual verts restore them     
            
            verts = for v = 1 to verts.count collect verts[vertorder[v]] *= sorttm; 
            
    -- change the verts and face indexing        
            
            meshop.setvert mObj #all verts;     
            for f = 1 to mObj.numfaces do -- set the faces to new order
            (
                face = getface mObj f;
                setface mObj f [indices[face.x],indices[face.y],indices[face.z]]
            )
            mObj; 
        )
    
        on execute do
        (    
            for i in selection where  canConvertTo i Editable_mesh do 
            (
                sortObj = copy i;
                if convertToMesh sortObj != undefined then
                    sortMeshVerts sortObj (MatrixFromNormal [0,0,1]);
            )    
        )
    )
    
  • Eric Chadwick
    Thanks for this! I am struggling with a looming deadline this week, hope to test this next week. I appreciate you sharing this!
  • Eric Chadwick
    I'm testing this today, and it sorts the vertices, except it does it the reverse of how I expected. The smallest vertex index is at the top, and the largest is at the bottom. But the inverse needs to be true for proper sorting from the Top view... the bottom vertices need to be first, followed by the rest as they rise upwards.

    I tried several tweaks to the script but nothing seemed to work properly, they always sorted from top to bottom, instead of bottom to top.

    I just don't have the scripting chops to handle this properly :(
  • Klunk
    Offline / Send Message
    Klunk ngon master
    no idea, works as intended here, lowest number verts at the bottom highest at the top.
  • rollin
    Offline / Send Message
    rollin polycounter
    if v1.z > v2.z then 1 else if v1.z < v2.z then -1 else 0;
    

    this line tells if it works upwards or downwards
    if v1.z > v2.z then -1 else if v1.z < v2.z then 1 else 0;
    

    this one should work the other way. (not tested)
Sign In or Register to comment.