Home Technical Talk

[MAXScript] View Stats

interpolator
Offline / Send Message
Revel interpolator
Hello guys. I tried to modified Mesh Info by Roger Hyde to looks the way I like, which is mimicking how modo looks like, here is the code so far:
---------------------------------------------------------------------
-- Based on Mesh Info by; Roger Hyde
-- http://www.scriptspot.com/3ds-max/scripts/meshinfo
---------------------------------------------------------------------

(
    function none =
    (
        if (selection.count == 1) then st = $.name
        else if (selection.count >= 2) then st = "Multiple Selected"
        else st = "None Selected"
        ss = stringstream ""
        format "%" st to:ss
        return ss as string
    )
    
    function getSceneFacesAsString =
    (
        tf = 0
        sf = 0
        for o in $geometry do
        (
            tf += o.mesh.numFaces
            if o.isSelected do sf += o.mesh.numFaces
        )
        tf = (dotNetClass "System.String").Format "{0:n0}" tf
        sf = (dotNetClass "System.String").Format "{0:n0}" sf
        ss = stringstream ""
        format "GL:  % (%)" tf sf to:ss
        return ss as string
    )
    
    function getNumVertsAsString obj =
    (
        try(
        tv = (dotNetClass "System.String").Format "{0:n0}" (obj.baseobject.mesh.numVerts)
        sv = (dotNetClass "System.String").Format "{0:n0}" (obj.baseobject.mesh.selectedverts.count)
        ss = stringstream ""
        format "Verts:  % (%)" sv tv to:ss
        return ss as string
        )catch()
    )
    
    function getNumEdgesAsString obj =
    (
        try(
        te = (dotNetClass "System.String").Format "{0:n0}" (obj.baseobject.mesh.edges.count)
        se = (dotNetClass "System.String").Format "{0:n0}" (obj.baseobject.mesh.selectededges.count)
        ss = stringstream ""
        format "Edges:  % (%)" se te to:ss
        return ss as string
        )catch()
    )
    
    function getNumFacesAsString obj =
    (
        try(
        tf = (dotNetClass "System.String").Format "{0:n0}" (obj.baseobject.mesh.numFaces)
        sf = (dotNetClass "System.String").Format "{0:n0}" (obj.baseobject.mesh.selectedFaces.count)
        ss = stringstream ""
        format "Tris:  % (%)" sf tf to:ss
        return ss as string
        )catch()
    )
    
    -- function getNumPolysAsString obj =
    -- (
        -- try(
        -- tp = (dotNetClass "System.String").Format "{0:n0}" (obj.baseobject.mesh.numFaces)
        -- sp = (dotNetClass "System.String").Format "{0:n0}" (obj.baseobject.mesh.selectedfaces.count)
        -- ss = stringstream ""
        -- format "Polys:  % (%)" sp tp to:ss
        -- return ss as string
        -- )catch()
    -- )
    
    function isValidObject obj =
    (
        validclasses = #(Editable_Mesh, Editable_Poly)
        for o in validclasses do
        (
            if classOf obj == o do return true
        )
        return false
    )
    
    function isMesh obj =
    (
        meshclasses = #(Editable_Mesh)
        for o in meshclasses do
        (
            if classOf obj == o do return true
        )
        return false
    )
    
    function gridSize =
    (
        try(
        if (activeGrid == undefined) then gs = gridPrefs.spacing
        else gs = activeGrid.grid
        case (units.SystemType) of
        (
            #Inches: (un = "in"; gs = 0.393701*gs)
            #Feet: (un = "ft"; gs = 0.0328084*gs)
            #Miles: (un = "mi"; gs = 6.21371e-006*gs)
            #Millimeters: (un = "mm"; gs = 10*gs)
            #Centimeters: (un = "cm"; gs = 1*gs)
            #Meters: (un = "m"; gs = 0.01*gs)
            #Kilometers: (un = "km"; gs = 1e-005*gs)
        )
        ss = stringstream ""
        format "Grid:  % %" gs un to:ss
        return ss as string
        )catch()
    )
    
    function drawInfo =
    (
        noneString = none() as string
        globalString = getSceneFacesAsString() as string
        vertsString = (getNumVertsAsString $) as string
        edgesString = (getNumEdgesAsString $) as string
        trisString = (getNumFacesAsString $) as string
        -- polysString = (getNumPolysAsString $) as string
        gridString = gridSize() as string
        
        noneExtent = gw.getTextExtent noneString
        globalExtent = gw.getTextExtent globalString
        vertsExtent = gw.getTextExtent vertsString
        edgesExtent = gw.getTextExtent edgesString
        trisExtent = gw.getTextExtent trisString
        -- polysExtent = gw.getTextExtent polysString
        gridExtent = gw.getTextExtent gridString
        
        nonePosX = (gw.getWinSizeX() - noneExtent.x - 10)
        nonePosY = (gw.getWinSizeY() - noneExtent.y - 40)
        globalPosX = (gw.getWinSizeX() - globalExtent.x - 10)
        globalPosY = (gw.getWinSizeY() - globalExtent.y - 40)
        vertsPosX = (gw.getWinSizeX() - vertsExtent.x - 10)
        vertsPosY = (gw.getWinSizeY() - vertsExtent.y - 40)
        edgesPosX = (gw.getWinSizeX() - edgesExtent.x - 10)
        edgesPosY = (gw.getWinSizeY() - edgesExtent.y - 40)
        trisPosX = (gw.getWinSizeX() - trisExtent.x - 10)
        trisPosY = (gw.getWinSizeY() - trisExtent.y - 40)
        -- polysPosX = (gw.getWinSizeX() - polysExtent.x - 10)
        -- polysPosY = (gw.getWinSizeY() - polysExtent.y - 40)
        gridPosX = (gw.getWinSizeX() - gridExtent.x - 10)
        gridPosY = (gw.getWinSizeY() - gridExtent.y - 40)
        
        none_line_pos = [nonePosX,nonePosY,0]
        global_line_pos = [globalPosX,globalPosY,0]
        verts_line_pos = [vertsPosX,vertsPosY,0]
        edges_line_pos = [edgesPosX,edgesPosY,0]
        tris_line_pos = [trisPosX,trisPosY,0]
        -- polys_line_pos = [polysPosX,polysPosY,0]
        grid_line_pos = [gridPosX,gridPosY,0]
        line_height = [0, 15, 0]
        
        if subobjectLevel != undefined then
        (
            if (subObjectLevel > 0) and (isValidObject (modPanel.getCurrentObject())) then
            (
                case subobjectlevel of
                (
                    1:
                    (
                        gw.wText verts_line_pos (getNumVertsAsString $) color:[255,199,71]
                        global_line_pos += line_height
                        gw.wText global_line_pos (getSceneFacesAsString()) color:[165,165,165]
                        grid_line_pos += (line_height)*3
                        gw.wText grid_line_pos (gridSize()) color:[165,165,165]
                    )
                    2:
                    (
                        gw.wText edges_line_pos (getNumEdgesAsString $) color:[255,199,71]
                        global_line_pos += line_height
                        gw.wText global_line_pos (getSceneFacesAsString()) color:[165,165,165]
                        grid_line_pos += (line_height)*3
                        gw.wText grid_line_pos (gridSize()) color:[165,165,165]
                    )
                    3:
                    (
                        if isMesh (modPanel.getCurrentObject()) then
                        (
                            (gw.wText tris_line_pos (getNumFacesAsString $) color:[255,199,71])
                            global_line_pos += line_height
                        )
                        else
                        (
                            gw.wText edges_line_pos (getNumEdgesAsString $) color:[255,199,71]
                            global_line_pos += line_height
                        )
                        gw.wText global_line_pos (getSceneFacesAsString()) color:[165,165,165]
                        grid_line_pos += (line_height)*3
                        gw.wText grid_line_pos (gridSize()) color:[165,165,165]
                    )
                    4:
                    (
                        gw.wText tris_line_pos (getNumFacesAsString $) color:[255,199,71]
                        global_line_pos += line_height
                        gw.wText global_line_pos (getSceneFacesAsString()) color:[165,165,165]
                        grid_line_pos += (line_height)*3
                        gw.wText grid_line_pos (gridSize()) color:[165,165,165]
                    )
                    5:
                    (
                        gw.wText tris_line_pos (getNumFacesAsString $) color:[255,199,71]
                        global_line_pos += line_height
                        gw.wText global_line_pos (getSceneFacesAsString()) color:[165,165,165]
                        grid_line_pos += (line_height)*3
                        gw.wText grid_line_pos (gridSize()) color:[165,165,165]
                    )
                )
                gw.enlargeUpdateRect #whole
                gw.updateScreen()
            )
            else
            (
                gw.wText none_line_pos (none()) color:[255,199,71]
                global_line_pos += line_height
                gw.wText global_line_pos (getSceneFacesAsString()) color:[165,165,165]
                grid_line_pos += (line_height)*3
                gw.wText grid_line_pos (gridSize()) color:[165,165,165]
                gw.enlargeUpdateRect #whole
                gw.updateScreen()
            )
        )
        else
        (
            gw.wText none_line_pos (none()) color:[255,199,71]
            global_line_pos += line_height
            gw.wText global_line_pos (getSceneFacesAsString()) color:[165,165,165]
            grid_line_pos += (line_height)*3
            gw.wText grid_line_pos (gridSize()) color:[165,165,165]
            gw.enlargeUpdateRect #whole
            gw.updateScreen()
        )
    )
    
    function startDrawInfo =
    (
        unregisterRedrawViewsCallback drawInfo
        registerRedrawViewsCallback drawinfo
        drawInfo()
        forceCompleteRedraw()
    )
    
    startDrawInfo()
)
It looks correct, but the problem is that the viewport redraw become lagging, like the text flickering when I do anything on the viewport. My maxscript knowledge is very limited so I have no idea how to improve the performance here, could anyone take a look and give some suggestion on how to improve this?

PS; I'm using Max 2009 at home, but in office using Max 2014 I don't think I remember any flickering issue, need to double confirm on this next Monday.

Replies

  • miauu
    Offline / Send Message
    miauu polycounter lvl 13
    In 3dsMax that uses Nitrous viewport there is no flickering. In 3dsMax 2009 the flickering is constant. The problem is in 3dsMax. You can't overcome it.
    About the code:

    this
    o.mesh.numfaces
    
    creates copy(in the memory) of the object each time when it is executed. In the same loop it is used twice. The same is valid for
    every xxxx.mesh.yyyy. And it is executed on each viewport redraw several times. For scene with several low poly objects this may not affet the performance, but....
  • Swordslayer
    Offline / Send Message
    Swordslayer interpolator
    You could simplify the code quite a bit, skip the .mesh part and only check deformable objects (you are already skipping shapes, you can skip primitives with no modifier apllied, too) and avoid creating so many stringstreams and doing things you can do once multiple times. Also the unit conversion as you do it would work only in a specific case, use the built-in one, it makes more sense to use the display units anyway:
    (
        unregisterRedrawViewsCallback ::drawInfo
    
        local stringFormat = (dotNetClass "System.String").Format
        local lineHeight = (getTextExtent #I).y + 2
        local yellowish = color 255 199 71
        local grayish = color 165 165 165
    
        function none =
            case selection.count of
            (
                0 : "None Selected"
                1 : $.name
                default : "Multiple Selected"
            )
    
        function getSceneFacesAsString =
        (
            local totalFaces = 0
            local selectedFaces = 0
            for obj in geometry where isDeformable obj do
            (
                totalFaces += obj.numFaces
                if obj.isSelected do selectedFaces += obj.numFaces
            )
            stringFormat "GL: {0:n0} ({1:n0})" totalFaces selectedFaces
        )
    
        function getNumVertsAsString obj =
            stringFormat "Verts:  {0:n0} ({1:n0})" (numPoints obj) obj.selectedVerts.count
    
        function getNumEdgesAsString obj =
            stringFormat "Edges:  {0:n0} ({1:n0})" obj.edges.count obj.selectedEdges.count
    
        function getNumFacesAsString obj =
            stringFormat "Faces:  {0:n0} ({1:n0})" obj.numFaces obj.selectedFaces.count
    
        function isValidObject obj =
            isKindOf obj Editable_Poly or isKindOf obj Editable_Mesh
    
        function isMesh obj =
            isKindOf obj Editable_Mesh
    
        function gridSize =
            "Grid:  " + units.formatValue (if activeGrid == undefined then gridPrefs.spacing else activeGrid.grid)
    
        function getTextLocation textExt winSizeX winSizeY lineMult =
            [winSizeX - textExt - 10, winSizeY - 40 + lineHeight * lineMult, 0]
    
        function getTextBox winSizeX winSizeY textExt =
            Box2 [winSizeX - textExt - 10, winSizeY - 40 + lineHeight * 2] [winSizeX, winSizeY]
    
        function redrawRectangle subObjsStr globalCountStr gridSizeStr =
        (
            local winSizeX = gw.getWinSizeX()
            local winSizeY = gw.getWinSizeY()
            local subObjsStrExt = (getTextExtent subObjsStr).x
            local globalCountStrExt = (getTextExtent globalCountStr).x
            local gridSizeStrExt = (getTextExtent gridSizeStr).x
    
            gw.wText (getTextLocation subObjsStrExt winSizeX winSizeY -1) subObjsStr color:yellowish
            gw.wText (getTextLocation globalCountStrExt winSizeX winSizeY 0) globalCountStr color:grayish
            gw.wText (getTextLocation gridSizeStrExt winSizeX winSizeY 2) gridSizeStr color:grayish
    
            gw.enlargeUpdateRect (getTextBox winSizeX winSizeY (amax subObjsStrExt globalCountStrExt gridSizeStrExt))
            gw.updateScreen()
        )
    
        function drawInfo =
        (
            local obj = modPanel.getCurrentObject()
    
            if subObjectLevel != undefined and subObjectLevel > 0 and isValidObject obj then
            (
                case subObjectLevel of
                (
                    1: redrawRectangle (getNumVertsAsString $) (getSceneFacesAsString()) (gridSize())
                    2: redrawRectangle (getNumEdgesAsString $) (getSceneFacesAsString()) (gridSize())
                    3:
                    (
                        if isMesh obj then redrawRectangle (getNumFacesAsString $) (getSceneFacesAsString()) (gridSize())
                        else redrawRectangle (getNumEdgesAsString $) (getSceneFacesAsString()) (gridSize())
                    )
                    4: redrawRectangle (getNumFacesAsString $) (getSceneFacesAsString()) (gridSize())
                    5: redrawRectangle (getNumFacesAsString $) (getSceneFacesAsString()) (gridSize())
                )
            )
            else redrawRectangle (none()) (getSceneFacesAsString()) (gridSize())
        )
    
        function startDrawInfo =
        (
            registerRedrawViewsCallback ::drawInfo
            setNeedsRedraw()
        )
    
        startDrawInfo()
    )
    
  • Revel
    Offline / Send Message
    Revel interpolator
    @miauu - thanks for the clarification man..hmm, that's bad too hear since at home I'm using max 2009 currently..ah well....

    @Swordslayer - wow! you really trim down the code! thanks!..but I notice that if I use;
    gw.enlargeUpdateRect (getTextBox winSizeX winSizeY (amax subObjsStrExt globalCountStrExt gridSizeStrExt))
    
    ..the display will gone when I do anything with the viewport but if I replace with "gw.enlargeUpdateRect #whole" it'll stay there. Any reason for this such behavior?

    But it clearly that your code make run run much more smoothly despite the constant flickering. Tested with a mesh around 1mil tris, the viewport didnt suffer with a slowdown as much as the previous code I wrote. It seems like related to miauu's point regarding the code creates a copy on a memory. Thanks so much! :)
  • Swordslayer
    Offline / Send Message
    Swordslayer interpolator
    Oops, wrong sign, the getTextBox body should have instead been:
    Box2 [winSizeX - textExt - 10, winSizeY - 40 - lineHeight * 2] [winSizeX, winSizeY]
    

    Sorry about that, looks like that doesn't have any effect in nitrous.
  • monster
    Offline / Send Message
    monster polycounter
    Revel, if you don't use .mesh you'll get the polygon count and not the triangle count.

    If you don't mind the polygon count you don't need to loop through all objects. You can just access the mesh totals from fileProperties.

    If you do want triangle counts keep in mind you don't need to access the .mesh property for vertex and edge totals.

    A while back when I created a viewport callback script I skipped calculations while the mouse was moving and a mouse button was pressed. This allowed the user to pan / orbit the view at full speed.
  • Revel
    Offline / Send Message
    Revel interpolator
    @Swordslayer - still not work man, it even gives an error and crashed the script. What does it do actually? using #whole seems like fine to me. Also I notice that when I have modifier (example; turbosmooth) ontop of my Editable Poly object, the verts and edges count always return 0 eventhough I'm on the base object..hmmm......

    @monster - yeah, you're right. I do want to display tris instead of polys so I added back .mesh for tris count. That's interesting idea to read only on mouse move, I tried to use mouse.mode since on the help file said; 0-idle, 1-point, 2-move, but no matter what I check (and move my mouse like crazy) while execute the script from the listener, it still return 0 (which means idle?), so how do you check that?
  • Swordslayer
    Offline / Send Message
    Swordslayer interpolator
    It limits viewport redraw only to the part where the text is (it doesn't work in nitrous, though), which would help with th flickering. I don't have 2009 to test with, works okay with max 9 though.

    Vertex/edge counters return 0 for me only for the 'selected' subobjects when Show End Result is toggled on (which is to be expected since you are getting the top result of the stack).

    By the way, since you are using .mesh once again, save the .mesh to a variable, get your stats and delete it afterwards.

    As for the mouse moving or not, you can save mouse position and check if it changed since the last time.
  • miauu
    Offline / Send Message
    miauu polycounter lvl 13
    For mouse moving - use mouse.buttonstates. It will tell you when the left mouse button is pressed or not.
  • Revel
    Offline / Send Message
    Revel interpolator
    Whoops sorry Swordslayer, my mistake! I mistakenly thought you asked me to change the line become:
    gw.enlargeUpdateRect (Box2 [winSizeX - textExt - 10, winSizeY - 40 - lineHeight * 2] [winSizeX, winSizeY])
    
    ..instead of assigning the value to the getTextBox haha! now everything works fine :)
    About the count being 0, I guess I can live with that, since the default Max's own statistic (default hotkey:7) also showed 0 when there is a modifier ontop.

    Eh? saving the .mesh as a variable? you mean like assigning;
    tempFaces = $.mesh.faces.count
    numFaces = tempFaces as integer
    tempFaces = undefined
    
    ..and use the numFace instead? wasn't too sure tough, better to confirm with you first before putting some junk code into my script ;)

    About the mouse position, I seems like can't get anything working, what miauu suggest to use mouse.buttonStates always return #{} or once I got #{3}. I found a really primitive way to check whether mouse is moving of not by declaring 3 variables;
    mouseInitPos = mouse.screenPos
    mousePos = a as point2
    mouseInitPos = undefines
    mouseNewPos = mouse.screenPos
    mouseInitPos == mouseNewPos
    
    ..so basically mouseInitPos will be a static number (initial position) and mouseNewPos will be the dynamic number (new moved position) and compare between the two, when mouse idle it'll return true, when moving will return false. So the problem is....where should I sneak it in?...man! scripting is so addicting!haha
  • RappyBMX
    function drawInfo = (
    
    to
    function drawInfo = if mouse.buttonstates.isEmpty do (
    
    also you could
    function drawInfo = if not isAnimPlaying() and mouse.buttonstates.isEmpty do (
    

    mesh to a variable like this
    _mesh = $.mesh
    
    and you call _mesh instead of $.mesh
    _mesh.verts.count
    _mesh.edges.count
    _mesh.faces.count
    
    you usually do this when you want to call the same thing more than once...
  • Revel
    Offline / Send Message
    Revel interpolator
    @RappyBMX - hmmm...theoretically it should work, but since mouse.buttonStates always gives empty array #{} no matter in what situation, the script will always run...until I right-clicked, the array will always return #{3} which makes the View Stats completely ignored but the code (since now it's not empty anymore).

    About assigning the $.mesh into a _mesh variable, is there any benefit by doing so? And I notice if using .mesh for an edge, it will return "wrong" value since 1 quad have 5 edges in mesh (instead of 4 in poly).
  • Swordslayer
    Offline / Send Message
    Swordslayer interpolator
    Not just saving it to a variable, deleting it afterwards, too. Launch task manager to see the RAM usage of 3dsmax process and see it rising with
    (
        local t = Teapot segments:64
        
        for i = 1 to 1000 do
            t.mesh.verts.count
    )
    

    and now compare it to
    (
        local t = Teapot segments:64
        local m -- one variable to hold them all, outside the loop/functions
        
        for i = 1 to 1000 do
        (
            m = t.mesh
            m.verts.count
            delete m
        )
    )
    

    The other things like checking mouse position.. well, I've taken the liberty to bend the code to fit that purpose once again, I'm not that good with explaining:
    (
        unregisterRedrawViewsCallback ::drawInfo
    
        local stringFormat = (dotNetClass "System.String").Format
        local lineHeight = (getTextExtent #I).y + 2
        local meshInfo
        local meshObj
        local prevPos
    
        struct meshInfoDef
        (
            yellowish = color 255 199 71,
            grayish = color 165 165 165,
    
            winSizeX, winSizeY,    textBox,
    
            lineSub = dataPair(),
            lineTotal = dataPair(),
            lineGrid = dataPair(),
    
            function getTextLocation textExt winSizeX winSizeY lineMult =
                [winSizeX - textExt - 10, winSizeY - 40 + lineHeight * lineMult, 0],
    
            function getTextBox winSizeX winSizeY textExt =
                Box2 [winSizeX - textExt - 10, winSizeY - 40 - lineHeight * 2] [winSizeX, winSizeY],
    
            function update subObjsStr globalCountStr gridSizeStr =
            (
                local subObjsStrExt = (getTextExtent subObjsStr).x
                local globalCountStrExt = (getTextExtent globalCountStr).x
                local gridSizeStrExt = (getTextExtent gridSizeStr).x
                local winSizeX = gw.getWinSizeX()
                local winSizeY = gw.getWinSizeY()
    
                textBox = getTextBox winSizeX winSizeY (amax subObjsStrExt globalCountStrExt gridSizeStrExt)
    
                lineSub.v1 = getTextLocation subObjsStrExt winSizeX winSizeY -1
                lineTotal.v1 = getTextLocation globalCountStrExt winSizeX winSizeY 0
                lineGrid.v1 = getTextLocation gridSizeStrExt winSizeX winSizeY 2
                lineSub.v2 = subObjsStr
                lineTotal.v2 = globalCountStr
                lineGrid.v2 = gridSizeStr
            ),
    
            function redraw =
            (
                gw.wText lineSub.v1 lineSub.v2 color:yellowish
                gw.wText lineTotal.v1 lineTotal.v2 color:grayish
                gw.wText lineGrid.v1 lineGrid.v2 color:grayish
    
                gw.enlargeUpdateRect textBox
                gw.updateScreen()
            )
        )
    
        function none =
            case selection.count of
            (
                0 : "None Selected"
                1 : $.name
                default : "Multiple Selected"
            )
    
        function getSceneTrisAsString =
        (
            local totalFaces = 0
            local selectedFaces = 0
    
            for obj in geometry where not isKindOf obj TargetObject do
            (
                meshObj = obj.mesh
                totalFaces += meshObj.numFaces
                if obj.isSelected do selectedFaces += meshObj.numFaces
                delete meshObj
            )
    
            stringFormat "GL: {0:n0} ({1:n0})" totalFaces selectedFaces
        )
    
        function getNumVertsAsString obj =
            stringFormat "Verts:  {0:n0} ({1:n0})" (numPoints obj) obj.selectedVerts.count
    
        function getNumEdgesAsString obj =
            stringFormat "Edges:  {0:n0} ({1:n0})" obj.edges.count obj.selectedEdges.count
    
        function getNumTrisAsString obj =
        (
            meshObj = obj.mesh
            local numTrisStr = stringFormat "Tris:  {0:n0} ({1:n0})" meshObj.numFaces meshObj.selectedFaces.count
            delete meshObj
            numTrisStr
        )
    
        function isValidObject obj =
            isKindOf obj Editable_Poly or isKindOf obj Editable_Mesh
    
        function isMesh obj =
            isKindOf obj Editable_Mesh
    
        function gridSize =
            "Grid:  " + units.formatValue (if activeGrid == undefined then gridPrefs.spacing else activeGrid.grid)
    
        function drawInfo =
        (
            local pos = mouse.screenPos
    
            if pos != prevPos do
            (
                local obj = modPanel.getCurrentObject()
    
                if subObjectLevel != undefined and subObjectLevel > 0 and isValidObject obj then
                (
                    case subObjectLevel of
                    (
                        1: meshInfo.update (getNumVertsAsString $) (getSceneTrisAsString()) (gridSize())
                        2: meshInfo.update (getNumEdgesAsString $) (getSceneTrisAsString()) (gridSize())
                        3:
                        (
                            if isMesh obj then meshInfo.update (getNumTrisAsString $) (getSceneTrisAsString()) (gridSize())
                            else meshInfo.update (getNumEdgesAsString $) (getSceneTrisAsString()) (gridSize())
                        )
                        4: meshInfo.update (getNumTrisAsString $) (getSceneTrisAsString()) (gridSize())
                        5: meshInfo.update (getNumTrisAsString $) (getSceneTrisAsString()) (gridSize())
                    )
                )
                else meshInfo.update (none()) (getSceneTrisAsString()) (gridSize())
    
                prevPos = pos
            )
            meshInfo.redraw()
        )
    
        function startDrawInfo =
        (
            meshInfo = meshInfoDef()
            registerRedrawViewsCallback ::drawInfo
            setNeedsRedraw()
        )
    
        startDrawInfo()
    )
    

    Basically, save everything to a struct, redraw with each viewport redraw, update only when mouse positon changes.
  • Revel
    Offline / Send Message
    Revel interpolator
    That sounds great, but Max throw me en error saying unable to convert:0 to type:string by highlighting the local subObjsStrExt = (getTextExtent subObjsStr).x :(
  • Swordslayer
    Offline / Send Message
    Swordslayer interpolator
    Should be already fixed (I've edited the cody shortly after posting).
  • Revel
    Offline / Send Message
    Revel interpolator
    Yeaps!now it's working fine! :)
    Another thing is, isit save to add forceCompleteRedraw() inside the drawInfo function? since currently the text will overlap each other before viewport redraw?

    I'm sorry man...I just....I just a little bit OCD when it comes about UI....
  • Swordslayer
    Offline / Send Message
    Swordslayer interpolator
    The trouble is I don't have max 2009 to test it on, nitrous handles things differently so things like this one are not apparent. Let's see, IIRC you can use gw.clearScreen for that (should be a better idea than redrawing views and trigger redrawViewsCallback again) though that one works kinda weird in nitrous. If you put gw.clearScreen textBox useBkg:true before gw.enlargeUpdateRect textBox does it work/fail/do funny things? Maybe also gw.resetUpdateRect() after gw.updateScreen() (doesn't do anything in nitrous, might have some impact in your case).
  • Revel
    Offline / Send Message
    Revel interpolator
    No worries man, I tested it on max 2014 as well but give a similar behavior (overlapped text) but clearly more faster (and slightly) faster viewport response operation over max 2009. I did try put the forceCompleteRedraw() it fix the problem with redraw text. Will try the code you gave just now and see how it looks ;)

    About max handling heavy object will slow down viewport, I guess it's just max being...max...hmm...

    Couldn't do all this without your help Swordslayer, thanks so much :)
  • miauu
    Offline / Send Message
    miauu polycounter lvl 13
    Revel, why do you think that the mouse.buttonstates not works?
    Watch this video to see which is better to be used.
  • Revel
    Offline / Send Message
    Revel interpolator
    Yeah miauu, your video explain it well. What I did before was typing the mouse.buttonStates on the listener and execute it while holding my left/mid/right button, all return #{ } or #{3} when holding the right click. Your solution does more simplified the vp redraw. But I'm just curious when pan/rotate/zoom, isit possible to stop the text redraw as well?

    I google around for a solution before and found something like this;
    rollout click_test "Click Test"
    (
        timer tick_tock interval:100 active:true
        label lbl_button "Idle State"
        local dnc_control
        local mouseButtonStates
        local leftBtn
        local midBtn
        local rightBtn
        local idleState
        
        on tick_tock tick do
        (
            mouseButtonStates = dnc_control.MouseButtons.value__
            theButtonStr = ""
            if (dotnet.CompareEnums mouseButtonStates leftBtn) do (idleState = true)
            if (dotnet.CompareEnums mouseButtonStates midBtn) do (idleState = true)
            if (dotnet.CompareEnums mouseButtonStates rightBtn) do (idleState = true)
            if idleState == true then
            (
                lbl_button.text = "Idle State"
                idleState = false
            )
            else
            (
                lbl_button.text = ""
            )
        )
        
        on click_test open do
        (
            dnc_control = dotNetClass "Control"
            mouseButtons = dnc_control.MouseButtons
            leftBtn = mouseButtons.Left.value__
            midBtn = mouseButtons.Middle.value__
            rightBtn = mouseButtons.Right.value__
        )
    )
    createDialog click_test
    
    ..not sure is it possible to use this on the code or not, if possible we need to implement it when "Idle State" is displayed, hold the redraw (which basically whenever any one of the mouse button pressed, hold the redraw). Just throwing some idea :)
  • Swordslayer
    Offline / Send Message
    Swordslayer interpolator
    Hmmm, and what about something like this?
    (
        ::drawInfoNodeCallback = undefined
        unregisterRedrawViewsCallback ::drawInfo
    
        local stringFormat = (dotNetClass "System.String").Format
        local lineHeight = (getTextExtent #I).y + 2
        local meshInfo
        local meshObj
    
        struct meshInfoDef
        (
            yellowish = color 255 199 71,
            grayish = color 165 165 165,
    
            winSizeX, winSizeY, textBox,
    
            lineSub = dataPair(),
            lineTotal = dataPair(),
            lineGrid = dataPair(),
    
            function getTextLocation textExt winSizeX winSizeY lineMult =
                [winSizeX - textExt - 10, winSizeY - 40 + lineHeight * lineMult, 0],
    
            function getTextBox winSizeX winSizeY textExt =
                Box2 [winSizeX - textExt - 10, winSizeY - 40 - lineHeight * 2] [winSizeX, winSizeY],
    
            function update subObjsStr globalCountStr gridSizeStr =
            (
                local subObjsStrExt = (getTextExtent subObjsStr).x
                local globalCountStrExt = (getTextExtent globalCountStr).x
                local gridSizeStrExt = (getTextExtent gridSizeStr).x
                local winSizeX = gw.getWinSizeX()
                local winSizeY = gw.getWinSizeY()
    
                textBox = getTextBox winSizeX winSizeY (amax subObjsStrExt globalCountStrExt gridSizeStrExt)
    
                lineSub.v1 = getTextLocation subObjsStrExt winSizeX winSizeY -1
                lineTotal.v1 = getTextLocation globalCountStrExt winSizeX winSizeY 0
                lineGrid.v1 = getTextLocation gridSizeStrExt winSizeX winSizeY 2
                lineSub.v2 = subObjsStr
                lineTotal.v2 = globalCountStr
                lineGrid.v2 = gridSizeStr
            ),
    
            function redraw =
            (
                gw.wText lineSub.v1 lineSub.v2 color:yellowish
                gw.wText lineTotal.v1 lineTotal.v2 color:grayish
                gw.wText lineGrid.v1 lineGrid.v2 color:grayish
    
                gw.enlargeUpdateRect textBox
                gw.updateScreen()
            )
        )
    
        function none =
            case selection.count of
            (
                0 : "None Selected"
                1 : $.name
                default : "Multiple Selected"
            )
    
        function getSceneTrisAsString =
        (
            local totalFaces = 0
            local selectedFaces = 0
    
            for obj in geometry where not isKindOf obj TargetObject do
            (
                meshObj = obj.mesh
                totalFaces += meshObj.numFaces
                if obj.isSelected do selectedFaces += meshObj.numFaces
                delete meshObj
            )
    
            stringFormat "GL: {0:n0} ({1:n0})" totalFaces selectedFaces
        )
    
        function getNumVertsAsString obj =
            stringFormat "Verts:  {0:n0} ({1:n0})" (numPoints obj) obj.selectedVerts.count
    
        function getNumEdgesAsString obj =
            stringFormat "Edges:  {0:n0} ({1:n0})" obj.edges.count obj.selectedEdges.count
    
        function getNumTrisAsString obj =
        (
            meshObj = obj.mesh
            local numTrisStr = stringFormat "Tris:  {0:n0} ({1:n0})" meshObj.numFaces meshObj.selectedFaces.count
            delete meshObj
            numTrisStr
        )
    
        function isValidObject obj =
            isKindOf obj Editable_Poly or isKindOf obj Editable_Mesh
    
        function isMesh obj =
            isKindOf obj Editable_Mesh
    
        function gridSize =
            "Grid:  " + units.formatValue (if activeGrid == undefined then gridPrefs.spacing else activeGrid.grid)
    
        function drawInfo updated:false =
        (
            if updated do
            (
                local obj = modPanel.getCurrentObject()
    
                if subObjectLevel != undefined and subObjectLevel > 0 and isValidObject obj then
                (
                    case subObjectLevel of
                    (
                        1: meshInfo.update (getNumVertsAsString $) (getSceneTrisAsString()) (gridSize())
                        2: meshInfo.update (getNumEdgesAsString $) (getSceneTrisAsString()) (gridSize())
                        3:
                        (
                            if isMesh obj then meshInfo.update (getNumTrisAsString $) (getSceneTrisAsString()) (gridSize())
                            else meshInfo.update (getNumEdgesAsString $) (getSceneTrisAsString()) (gridSize())
                        )
                        4: meshInfo.update (getNumTrisAsString $) (getSceneTrisAsString()) (gridSize())
                        5: meshInfo.update (getNumTrisAsString $) (getSceneTrisAsString()) (gridSize())
                    )
                )
                else meshInfo.update (none()) (getSceneTrisAsString()) (gridSize())
            )
            meshInfo.redraw()
        )
        
        function updateDrawInfo evnt arg =
            drawInfo updated:true
    
        function startDrawInfo =
        (
            meshInfo = meshInfoDef()
            registerRedrawViewsCallback ::drawInfo
            ::drawInfoNodeCallback = NodeEventCallback mouseUp:true all:updateDrawInfo
            drawInfo updated:true
            setNeedsRedraw()
        )
    
        startDrawInfo()
    )
    
  • Revel
    Offline / Send Message
    Revel interpolator
    Sorry, just can test this now, was busy with another script since yesterday :)
    Hmmm...now it seems like too less redraw, when selecting subobject even after I mouse up, it still didn't redraw the new number until I pan/rotate/zoom the viewport, tested on Max 2014. How is it on your end?

    EDIT: Play with it a little bit more, by adding a completeRedraw() below meshInfo.redraw(), makes it better :)
  • Revel
    Offline / Send Message
    Revel interpolator
    Guys, on this few post before I posted a code about utilizing a dotNet stuff. Isit possible to extract it to work as a function without the rollout?
    on click_test open do
    
    ..this bits, what should I replaced it with if Ii want to use it without a rollout? or is it even possible? I run out of a keyword to search on the Maxscript help still couldn't find anything close for a solution =__=

    Was thinking of using it something like this;
    global glRevelTemp
    
    (
        struct stRevelTemp
        (
            fn mouseBtn =
            (
                timer tick_tock interval:100 active:true
                local dnc_control
                local mouseButtonStates
                local leftBtn, midBtn, rightBtn
                local leftClicked, midClicked, rightClicked
                
                on tick_tock tick do
                (
                    mouseButtonStates = dnc_control.MouseButtons.value__
                    if (dotnet.CompareEnums mouseButtonStates leftBtn) do (leftClicked = true)
                    if (dotnet.CompareEnums mouseButtonStates midBtn) do (midClicked = true)
                    if (dotnet.CompareEnums mouseButtonStates rightBtn) do (rightClicked = true)
                    case of
                    (
                        (leftClicked == true): (print "left"; leftClicked = false; return #left)
                        (midClicked == true): (print "mid"; midClicked = false; return #mid)
                        (rightClicked == true): (print "right"; rightClicked = false; return #right)
                    )
                )
                
                on ??? do
                (
                    dnc_control = dotNetClass "Control"
                    mouseButtons = dnc_control.MouseButtons
                    leftBtn = mouseButtons.Left.value__
                    midBtn = mouseButtons.Middle.value__
                    rightBtn = mouseButtons.Right.value__
                )
            )
        )
        
        glRevelTemp = stRevelTemp()
    )
    
    ..so intended to use it as glRevelTemp.mouseBtn() and return either #left/ #mid/ #right...but the "???" obviously stop the code from being any useful...
Sign In or Register to comment.