Announcement

Collapse
No announcement yet.

A flexible one-liner for non-scripters

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • A flexible one-liner for non-scripters

    I often get asked to help making mass adjustments to scenes, or to provide tools to do that.
    While all too happy to help, i'd be much happier knowing i passed something along other than one solution to a single, specific problem.
    In that spirit, let me share with you what is possibly the awesomest one-liner of them all: let's change something in a bunch of things only if a condition is satisfied.

    For example, let's say that you would like to change all the V-Ray standard materials in your scene, which currently have a very un-physical reflection glossiness of 1.0 to a slightly lower value, say 0.995, so to enable a more comprehensive sampling and help avoid fireflies.
    The script would run like so, nearly in natural English language (brackets added for clarity only.):

    Code:
    [B]for[/B] [I]myMaterial [/I][B]in[/B] ([B]getClassInstances vraymtl[/B]) [B]where[/B] ([I]myMaterial[/I].[B]reflection_glossiness == [/B]1.0)[B] do[/B] ([I]myMaterial[/I].[B]reflection_glossiness =[/B] 0.995)
    *) So, the script would collect all the vrayMtl class materials (getClassInstances vraymtl) into a list (an Array, to be precise), and then run through that to assign them one after the other to the variable "myMaterial" for further processing.

    *)The keyword "where" works like in natural language: it needs a condition to be satisfied, and if it is, then it will proceed.
    In this case, we ask Max to check each material reflection glossiness level: if it is 1.0 then the material needs to be acted upon.

    *)You will want to pay particular attention to the DOUBLE EQUAL SIGN: that is, in coding lingo, a QUESTION.
    In other words, myMaterial.reflection_glossiness == 1.0, would read "is myMaterial reflection_glossiness equal to 1.0?".

    *)If the answer to that question is "yes" (True, or 1) then the script will proceed DOing something.
    It will, precisely, execute anything present in the brackets after the keyword "do", one line at a time.
    The original goal was to change reflection glossiness amount, and so we proceed setting it.

    *)Notice the SINGLE EQUAL SIGN: that's an ASSIGNMENT in coding.
    In other words, now we tell max to SET the value of the property to 0.995

    And this is the lay of the land. What's so special about this is that with the help of the maxscript listener, and its Macro Recorder, you can easily mass modify pretty much anything: lights, objects, maps, you name it.
    For example, let's say i want to scale down all lights which are wider than 10 units:
    first, select one light.
    then type
    Code:
    classOf $
    in the listener, followed by the keypad enter.
    $ in maxscript means "what is currently selected", and the command "classOf" queries the class of nodes.
    it will print "vrayLight", which is the class of objects we want to operate on.
    with the light still selected and the listener open, turn on the macro recorder (macro recorder menu -> enable), and change with the standard UI the light width.
    The listener will print "$.size1 = 1.234".
    The name after the dot is the property we're interested in: "size1"

    Code:
    for myLight in (getClassInstances vrayLight) where (myLight.size1 > 10.0) do (myLight.size1 = 10.0)
    Or let's turn all vrayHDRI loader maps filtering to elliptical, if they do not have it currently:
    Code:
    for myMap in (getClassInstances vrayHDRI) where (myMap.filterMode == 0) do (myMap.filtermode = 1)
    Or let's select all proxies for which no file is loaded
    Code:
    for myProxy in (getClassInstances vrayProxy) where (myProxy.filename == "") do (selectMore myProxy)
    I believe this is enough to start a conversation.
    I purposefully simplified things, and left a few tricky parts unsaid, along with some redundant code, but they may be better suited to a more in-depth debate, should it arise at all.

    happy mass changes!
    Last edited by ^Lele^; 01-11-2019, 12:57 AM.
    Lele
    Trouble Stirrer in RnD @ Chaos
    ----------------------
    emanuele.lecchi@chaos.com

    Disclaimer:
    The views and opinions expressed here are my own and do not represent those of Chaos Group, unless otherwise stated.

  • #2
    Very helpful thanks Lele!
    Chris Jackson
    Shiftmedia
    www.shiftmedia.sydney

    Comment


    • #3
      Bookmarked ! =D
      Thanks for sharing
      -------------------------------------------------------------
      Simply, I love to put pixels together! Sounds easy right : ))
      Sketchbook-1 /Sketchbook-2 / Behance / Facebook

      Comment


      • #4
        That is REALLY helpful, thank you!
        Martin
        http://www.pixelbox.cz

        Comment


        • #5
          and now for maya
          e: info@adriandenne.com
          w: www.adriandenne.com

          Comment


          • #6
            Tricky part #1: the VRayHDRI line has a redundant (ie. unneeded) condition.
            It works, but as it is is neither quicker, nor future-proof.
            The reason is that at the moment we only have 2 options for the filtering mode: isotropic and elliptical.
            The possible choices for the filterMode property will as such only be 0 (isotropic) or 1.
            So, let's say we want to make it quick: we can simply remove the condition entirely, and tell max to change all maps to elliptical.
            If they were already as such, nothing will happen, and if they weren't, they'll be changed.
            Code:
            for myMap in (getClassInstances vrayHDRI) do (myMap.filtermode = 1)
            Let's say we want to make the script future proof, and have it work right if in the future a third mode was added to the filtering.
            In that case, we'd like to leave those we set manually to the newer filtering alone.
            It stands to reason that the new mode should be numbered with a "2" ("0" for isotropic, "1" for elliptical).
            So, for any mode which is NOT 2, please set it to 1.
            Code:
            for myMap in (getClassInstances vrayHDRI) where (myMap.filterMode != 2) do (myMap.filtermode = 1)
            This is a chance to get introduced to the "not equal to", or "different to" symbol: !=
            Often there is no single way to do this right: the condition could be also (myMap.filterMode <=1 ), or (myMap.filterMode < 2), and the line would work just as well.
            Experiment as you see fit.
            The quickest of you will have spotted that it would also have worked in future-proof mode with the original condition (myMap.filterMode == 0), but i had a point to make. :P

            Tricky part #2: the proxy line will not work, and max will spit a strange error: "No ""selectmore"" function for VRayProxy".
            This is normal, and to cure it, i will have to introduce you to a new way to collect the stuff you want to operate on: filtered collection.
            Instead of getting a class instance, we'll get the actual node.
            Proxies are of the geometry superclass (no, really. Consider it the parent class. It has no cape, it doesn't fly.), and we can easily filter all of the geometry nodes down to the type we need, like so:

            Code:
            for g in geometry where classof g == vrayproxy collect g
            You've already seen all of this before, minus the self-explanatory "collect" command.
            The line above would however collect the proxies into, well, nothing at all.
            It would need to have the list become permanent through assignment to a variable (myProxies = for g in geometry...), or as we did before for the instances, be used as it's ready: inlined.

            The final one-liner then would look rather long, but surely not daunting.
            It may help if, while mentally reading it, you used pauses like you would if you found a comma in a standard sentence, only here when you see a set of brackets.
            Code:
            for myProxy in (for g in geometry where classof g == vrayproxy collect g) where (myProxy.filename == "") do (selectMore myProxy)
            Notice it would need the first set of brackets, unlike before, or the script won't work.

            I find it a mess to read, so i generally prefer a modicum of logical and visual order to simple typing speed, and assign it to a variable first (myProxies), and reuse that variable one line later:
            Code:
            myProxies = for g in geometry where classof g == vrayproxy collect g
            for myProxy in myProxies where (myProxy.filename == "") do (selectMore myProxy)
            As for Maya, it'd be best if it wasn't I doing that, eheh.
            Lele
            Trouble Stirrer in RnD @ Chaos
            ----------------------
            emanuele.lecchi@chaos.com

            Disclaimer:
            The views and opinions expressed here are my own and do not represent those of Chaos Group, unless otherwise stated.

            Comment


            • #7
              Thank you so much Lele! This is exactly what I was wondering about recently. To slowly get into some coding to make (in my case in Maya to make my life easier I am sure it is not so different than maxscript, after all the logic should be the same Thanks again
              My Artstation
              Whether it is an advantageous position or a disadvantageous one, the opposite state should be always present to your mind. -
              Sun Tsu

              Comment


              • #8
                thank you Lele. Precious stuff.
                Marcin Piotrowski
                youtube

                Comment


                • #9
                  Hey,
                  just got a little question here: I want to mass delete the custom attribute we get on each object during import. I found out how it's done for a single object, but I can not figure out how to do this on thousands of objects. The line for a single object is this:

                  for i = custAttributes.count $ to 1 by -1 do custAttributes.delete $ i

                  Also, I want to delete the user defined properties of each object in the object properties. How do I do that?
                  https://www.behance.net/Oliver_Kossatz

                  Comment


                  • #10
                    You'd need a loop within a loop for the first one ( where $ is replaced by a variable containing the object from your collection of nodes), while you can use an empty string to wipe the user properties on geometry: see here.

                    So for the first case it would go something like
                    Code:
                    (
                    myGeo = for g in geometry collect g
                    for g in mygeo do
                    (
                    for i = custAttributes.count g to 1 by -1 do custAttributes.delete g i
                    )
                    )
                    while for the second case you'd do something like
                    Code:
                    (
                    myGeo = for g in geometry collect g //this collects ALL the scene geo. maybe you could go by selection instead, or layer...
                    for g in myGeo do 
                    (
                    setUserPropBuffer g ""
                    )
                    )
                    let me know if this helps any.
                    Lele
                    Trouble Stirrer in RnD @ Chaos
                    ----------------------
                    emanuele.lecchi@chaos.com

                    Disclaimer:
                    The views and opinions expressed here are my own and do not represent those of Chaos Group, unless otherwise stated.

                    Comment


                    • #11
                      This works as expected. Thanks heaps Lele!
                      https://www.behance.net/Oliver_Kossatz

                      Comment


                      • #12
                        Great. This is very helpful! Thank you for this explanation.
                        for my blog and tutorials:
                        www.alfasmyrna.com

                        Comment


                        • #13
                          One other helpful bit to spend some time on: the all-important "show" command.

                          It's the short form of the command "showProperties", and it does just that: given a node, it will print its properties in the listener (or readable/writable parameter set. In other words, the stuff one needs to edit to have an effect on anything.).

                          For example, let's try and figure out the name of the reflection glossiness texture slot in a V-Ray material, because we really need to clear that out from all materials in our scene.
                          I'll comment in-line this time: anything after "--" in the code below is a comment for humans to read, and won't be executed by Max.
                          First, let's query the properties of the vrayMtl
                          Code:
                          (                                   -- start of the local scope. [I]What goes on in the local scope, stays in the local scope.[/I] [I]Use a set of brackets around your scripts whenever you can[/I]!
                               myTempMaterial = vrayMtl()     --notice the brackets after "vrayMtl". those ensure an instance of the class is created. It's then assigned to (stored into) the variable "myTempMaterial", on the other side of the equal sign
                               show myTempMaterial            --this line will print out all the properties of whatever was saved inside that variable. In our case, the vrayMtl.
                          )
                          Short form of the above:
                          Code:
                          show (vrayMtl())  --in this case we create a throwaway version of a vrayMtl, stored nowhere in particular, which will disappear after having been queried by the show command. We only need the printout of the parameters, after all.
                          Now, brace for some text. Know, however, that within a few seconds you'll be an expert reading it.
                          Each line is a property of our material (or "node", in general).
                          Each property is listed with its dot at the beginning (f.e. ".diffuse". We access node properties like this: "myMaterial.diffuse". So it's a nice copy/paste shortcut to have the dot present.), and a description of what kind of parameter it expects, after the columns (f.e. RGB Color, Float, Boolean, and so on.).
                          Parameters with a set of brackets after the name (f.e. reflection_exitColor (reflect_exitColor)) show also an Alias to the parameter name (in other words, one can use either form to query/modify a parameter.).

                          Here then is the result of either of the above scripts:

                          Code:
                            .diffuse : RGB color
                            .diffuse_roughness : float
                            .selfIllumination : RGB color
                            .selfIllumination_gi : boolean
                            .selfIllumination_multiplier : float
                            .compensate_camera_exposure : boolean
                            .reflection : RGB color
                            .reflection_glossiness : float
                            .hilight_glossiness (notUsed) : float
                            .reflection_subdivs : integer
                            .reflection_fresnel : boolean
                            .reflection_maxDepth : integer
                            .reflection_exitColor (reflect_exitColor) : RGB color
                            .reflection_useInterpolation (useInterpolation) : boolean
                            .reflection_ior : float
                            .reflection_metalness : float
                            .reflection_lockGlossiness : boolean
                            .reflection_lockIOR : boolean
                            .reflection_dimDistance : worldUnits
                            .reflection_dimDistance_on : boolean
                            .reflection_dimDistance_falloff : float
                            .reflection_affectAlpha : integer
                            .refraction : RGB color
                            .refraction_glossiness : float
                            .refraction_subdivs : integer
                            .refraction_ior : float
                            .refraction_fogColor : RGB color
                            .refraction_fogMult (refraction_fogMultiplier) : float
                            .refraction_fogBias : float
                            .refraction_affectShadows : boolean
                            .refraction_affectAlpha : integer
                            .refraction_maxDepth : integer
                            .refraction_exitColor (refract_exitColor) : RGB color
                            .refraction_useExitColor (refract_useExitColor) : boolean
                            .refraction_useInterpolation : boolean
                            .refraction_dispersion (refract_dispersion) : float
                            .refraction_dispersion_on (refract_dispersion_on) : boolean
                            .translucency_on (translucency_type) : integer
                            .translucency_thickness : worldUnits
                            .translucency_scatterCoeff : float
                            .translucency_fbCoeff : float
                            .translucency_multiplier : float
                            .translucency_color (translucent_color) : RGB color
                            .brdf_type : integer
                            .anisotropy (brdf_anisotropy) : float
                            .anisotropy_rotation (brdf_anisotropy_rotation) : float
                            .anisotropy_derivation (brdf_anisotropy_derivation) : integer
                            .anisotropy_axis (brdf_anisotropy_axis) : integer
                            .anisotropy_channel (brdf_anisotropy_channel) : integer
                            .soften (brdf_soften) : float
                            .brdf_fixDarkEdges : boolean
                            .gtr_gamma : float
                            .gtr_oldGamma : boolean
                            .brdf_useRoughness (option_useRoughness) : boolean
                            .option_traceDiffuse : boolean
                            .option_traceReflection : boolean
                            .option_traceRefraction : boolean
                            .option_doubleSided : boolean
                            .option_reflectOnBack : boolean
                            .option_useIrradMap : boolean
                            .refraction_fogUnitsScale_on (refraction_fogUnitScale_on) : boolean
                            .option_traceDiffuseAndGlossy : integer
                            .option_cutOff : float
                            .preservationMode : integer
                            .option_environment_priority : integer
                            .effect_id : integer
                            .override_effect_id : boolean
                            .option_clampTextures : boolean
                            .option_opacityMode : integer
                            .option_glossyFresnel : boolean
                            .option_diffuse_roughness_model : integer
                            .texmap_diffuse : texturemap
                            .texmap_diffuse_on : boolean
                            .texmap_diffuse_multiplier : float
                            .texmap_reflection : texturemap
                            .texmap_reflection_on : boolean
                            .texmap_reflection_multiplier : float
                            .texmap_refraction : texturemap
                            .texmap_refraction_on : boolean
                            .texmap_refraction_multiplier : float
                            .texmap_bump : texturemap
                            .texmap_bump_on : boolean
                            .texmap_bump_multiplier : float
                            .texmap_reflectionGlossiness : texturemap
                            .texmap_reflectionGlossiness_on : boolean
                            .texmap_reflectionGlossiness_multiplier : float
                            .texmap_refractionGlossiness : texturemap
                            .texmap_refractionGlossiness_on : boolean
                            .texmap_refractionGlossiness_multiplier : float
                            .texmap_refractionIOR (texmap_ior) : texturemap
                            .texmap_refractionIOR_on (texmap_ior_on) : boolean
                            .texmap_refractionIOR_multiplier (texmap_ior_multiplier) : float
                            .texmap_displacement : texturemap
                            .texmap_displacement_on : boolean
                            .texmap_displacement_multiplier : float
                            .texmap_translucent : texturemap
                            .texmap_translucent_on : boolean
                            .texmap_translucent_multiplier : float
                            .texmap_environment : texturemap
                            .texmap_environment_on : boolean
                            .texmap_hilightGlossiness : texturemap
                            .texmap_hilightGlossiness_on : boolean
                            .texmap_hilightGlossiness_multiplier (notUsed) : float
                            .texmap_reflectionIOR : texturemap
                            .texmap_reflectionIOR_on : boolean
                            .texmap_reflectionIOR_multiplier : float
                            .texmap_opacity : texturemap
                            .texmap_opacity_on : boolean
                            .texmap_opacity_multiplier : float
                            .texmap_roughness : texturemap
                            .texmap_roughness_on : boolean
                            .texmap_roughness_multiplier : float
                            .texmap_anisotropy : texturemap
                            .texmap_anisotropy_on : boolean
                            .texmap_anisotropy_multiplier : float
                            .texmap_anisotropy_rotation : texturemap
                            .texmap_anisotropy_rotation_on : boolean
                            .texmap_anisotropy_rotation_multiplier : float
                            .texmap_refraction_fog (texmap_fog) : texturemap
                            .texmap_refraction_fog_on (texmap_fog_on) : boolean
                            .texmap_refraction_fog_multiplier (texmap_fog_multiplier) : float
                            .texmap_self_illumination (texmap_selfIllumination) : texturemap
                            .texmap_self_illumination_on (texmap_selfIllumination_on) : boolean
                            .texmap_self_illumination_multiplier (texmap_selfIllumination_multiplier) : float
                            .texmap_gtr_tail_falloff : texturemap
                            .texmap_gtr_tail_falloff_on : boolean
                            .texmap_gtr_tail_falloff_multiplier : float
                            .texmap_metalness : texturemap
                            .texmap_metalness_on : boolean
                            .texmap_metalness_multiplier : float
                          If you recall, we wanted to get rid of the reflection glossiness texture: you'll notice there is an ample section of parameters, near the bottom, with "texmap_" in the name.
                          Sure enough, there are three parameters which look promising:
                          .texmap_reflectionGlossiness : texturemap
                          .texmap_reflectionGlossiness_on : boolean
                          .texmap_reflectionGlossiness_multiplier : float


                          The first, as can be deduced, contains the texture map itself (or a value of "undefined" if no texture was plugged in.).
                          The second and third parameters, however, determine if the map is active (boolean value: on/off, true/false, 0/1), and the map influence amount (float value: 75.3, 100.0, 0.0 and so on).

                          So, we can do one of three things to turn off that map: delete the map entirely, turn off the map (which remains in its place) or zero its influence.

                          Three little scripts then, for each of the above cases.
                          Let's wipe the maps entirely from the scene first:
                          Code:
                          (
                              myMaterials = getclassinstances vrayMtl                                   --Collecting all the V-ray materials in the scene
                              for myMat in myMaterials do myMat.texmap_reflectionGlossiness = undefined --assigning "undefined" as texture for that slot deletes it entirely
                          )
                          Second, let's be more conservative and just turn those off
                          Code:
                          (
                              myMaterials = getclassinstances vrayMtl                                  --Collecting all the V-ray materials in the scene
                              for myMat in myMaterials do myMat.texmap_reflectionGlossiness_on = false --The "on" property set to false means "off"
                          )
                          Third, let's do it through the map influence spinner

                          Code:
                          (
                              myMaterials = getclassinstances vrayMtl                                        --Collecting all the V-ray materials in the scene
                              for myMat in myMaterials do myMat.texmap_reflectionGlossiness_multiplier = 0.0 --Setting map influence to 0.0
                          )
                          We have already seen how to "inline" things, so to return to one-liners, here's how to turn off (not delete!) the map for the reflection glossiness:
                          Code:
                          (
                              for myMat in (getclassinstances vraymtl) do myMat.texmap_reflectionGlossiness_on = false
                          )
                          This was a lot of reading, but the show command is really quite useful.
                          For example,

                          Code:
                          show renderers.current
                          Will print out all the accessible parameters for your current renderer, so that you may be able to tweak things even if no UI is present.
                          Feel free to experiment, ideally in a safe situation (don't blame me if you disintegrate a production scene which needed to be out of the door instead! ).
                          Last edited by ^Lele^; 30-10-2019, 05:12 AM.
                          Lele
                          Trouble Stirrer in RnD @ Chaos
                          ----------------------
                          emanuele.lecchi@chaos.com

                          Disclaimer:
                          The views and opinions expressed here are my own and do not represent those of Chaos Group, unless otherwise stated.

                          Comment


                          • #14
                            Thanks a lot
                            for my blog and tutorials:
                            www.alfasmyrna.com

                            Comment

                            Working...
                            X