Home Technical Talk

Mel Scripting Help

1
polycounter lvl 10
Offline / Send Message
gilesruscoe polycounter lvl 10
For my university assignment i have to make a script that scatters tree's across a scene randomly. The script has to include:

· "Simple User Interface (GUI) to include the following options:
[FONT=&quot]o [/FONT]Select species of tree to use – i.e. Oak / Beech / Pine etc
[FONT=&quot]o [/FONT]Number of trees to generate
[FONT=&quot]o [/FONT]Size (radius) of the forest
[FONT=&quot]o [/FONT]Placement mode - i.e. Grid or Random
[FONT=&quot]o [/FONT]Create a ‘Populate’ button and connect it to a procedure to automatically duplicate and place the trees within the scene using the selected ‘placement mode’.
Additional marks will be awarded for providing the user with a suitable mechanism to identify multiple tree species to use. At each iteration of the ‘Populate’ procedure, a tree species from the list should be randomly selected and used to generate and place each new object."

While i've gotten most of it sorted with the GUI and placements etc there are a few things i'm unsure of how to do.

First thing is scattering randomly and stopping the trees from placing ontop of each other. My teacher went over poisson disc and jittered placements but never went into how to actually go about writing code for it. I'd also like to include some extra functions, like if the scene has a floorplane which is uneven (like terrain) that the tree's are placed on the surface of the floorplane. I'd also like to include a "custom mesh" option, where instead of placing the preset trees you can enter the name of an object in the scene into a text field and duplate that object instead.

This is what i've got for the placement at the moment, just a basic random value scatter, but the meshes often land ontop of each other..

int $i; for ($i = 0; $i<100; $i++ )
{
float $x = rand(0, 100);
float $z = rand(0, 100);
polyCylinder;
move $x 0 $z;
}


I've also not been able to find many good sources of info/help about MEL online (other than the autodesk command reference) does anybody know good books/websites for learning the basics?

Replies

  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    Oh and im also having trouble getting my sliders to work using percentages.

    i want each slider to control the percentage of each type of tree to use in the scene. To do this i thought that i could take the first slider value, minus it from 100, and use that new value as the max value for the next slider, and repeat for the 3rd slider. I thought i had a way of doing it figured out but i can't get it to work... i think i'm going about this completely the wrong way :'(

    ///////// columnLayout -adjustableColumn true;
    intSliderGrp -label "Oak%" -field true
    -fieldMinValue -0 -fieldMaxValue 100.0
    -value 0;
    //percentage calc
    //take number from first slider, minus it from 100 and set that value to be
    //the max value for next slider, repeat twice
    int $max1 =`intSliderGrp -query -value sliderOak%`;
    return $max1
    int $hundred = 100
    $hundred - $max1 = int $result1
    columnLayout -adjustableColumn true;
    intSliderGrp -label "Maple%" -field true
    -fieldMinValue -0 -fieldMaxValue - $result1
    -value 0;
    int $max2 =`intSliderGrp -query -value sliderMaple%`;
    return $max2
    $result1 - max2 = int $result2
    columnLayout -adjustableColumn true;
    intSliderGrp -label "Maple%" -field true
    -fieldMinValue -$result2 -fieldMaxValue - $result2
    -value $result2;
    ////////
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    Bump for the sake of bumping, any tips/help would be awesome.
  • Illusions
    Options
    Offline / Send Message
    Illusions polycounter lvl 18
    Why not just total the value of all sliders, and then normalize that number to 100%? Going for exact percentages just seems like more trouble than its worth, and is going to slow down the user, and require you to normalize it anyway, or write a script to redistribute unused percentages, if the percentages they've set are less than 100%. Prompt them about this with text. For instance:

    You have assigned 94% of the Total Tree Population. You can still distribute 6%. If you proceed with generating your Tree Population, YourScriptName will normalize these values for you.

    or if they over-assign:

    You have over-assigned the Total Tree Population by 4%. If you proceed with generating your Tree Population, YourScriptName will normalize these values for you.

    To come up with a percentage, just have each slider go from 0 to 100, then total all sliders, and divide 100 by that total, then multiply the value of each slider by that number. If its less than 100, you'll be multiplying by a number larger than 1, at 100 by 1, and above 100 by a fraction between 0 and 1. Make sure to not bother with this calculation at all if they entered 0 for each slider value, since you'd be dividing by 0.
  • fade1
    Options
    Offline / Send Message
    fade1 polycounter lvl 14
    reply to post #1:
    maybe it'S smarter to randomly assign them to the vertices of your plane. check for dupes and you're fine. of course you might not get the denseness you like, but a helper mesh with more verts could do the job... there are a bunch of scripts on creativecrash.com who do exactly this(search for scatter..). look into the source code and get the infos you need. it's always smart to use stuff which already works, intead of reinvent the wheel. saves time and you learn faster ;)
    plus also ask on tech-artists.org, a got resource if it goes deeper into coding/scripting issues.
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    @illusions, i'm not sure how that would work though? I need there to be three different percentages to control three different types of tree surely?

    @fade1, the script is required to work without a floor plane (just blank maya scene) and then i'd like to make it work with a floor plane too. Maybe it would be possible for the script to add a plane, add the trees to the verts, delete the plane? And then i can control the trees by setting values for the plane that is made at first? How would i go about doing that?
  • Froyok
    Options
    Offline / Send Message
    Froyok greentooth
    Making a plane is very simple, simply use the maya command for creating the plane, and add inputs for getting the number of subdivisions that you want ! :)
    Then you do a query for getting the location of every verts and you place your tree.
    At the end you simply delete the plane.
    Since you can give a name at everything, give a clear name to your plane for being able to delete it later.

    Ex :
    polyPlane "myPlane";
    
    [do your stuff]
    
    select -r "myPlane"; //replace the selection by your plane
    delete;
    
    http://download.autodesk.com/us/maya/2011help/Commands/polyPlane.html
    http://download.autodesk.com/us/maya/2011help/Commands/delete.html
  • haiddasalami
    Options
    Offline / Send Message
    haiddasalami polycounter lvl 14
    Could store the vertices in an array then do a random number (believe the command is rand) and access the array with the random number. A quick way to check would be to store the vertices used in another array and see if the two match up, if not make tree else generate new number.

    You could also do this without a plane (as you wrote), and just randomize x and z while keeping a constant y as 0 if your pivot is at the bottom. Just swap the array with vertices used with the vector locations of the trees
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    Right, this is actually starting to go somewhere now!

    This is what i have so far, met another problem though.

    ///////////////
    global string $Density;
    global string $Area;
    window -title "Giles Tree Gen V 0.1"
    -iconName "Tree Gen"
    -widthHeight 300 300;
    columnLayout -adjustableColumn true;


    $Density = `intSliderGrp -label "Density"
    -value 10
    -fieldMinValue 0
    -fieldMaxValue 100.0
    -field true`;
    $Area = `intSliderGrp -label "Area (Grid Units)"
    -value 10
    -fieldMinValue 1
    -fieldMaxValue 100.0
    -field true`;
    columnLayout -adjustableColumn true;
    button -label "Random Place" -command "Randomplace()";
    button -label "Close" -command ("deleteUI -window " + $window);

    showWindow $window;
    proc Randomplace()
    {
    global string $Density;
    int $v = `intSliderGrp -q -value $Density`;
    global string $Area;
    int $scale = `intSliderGrp -q -value $Area`;
    int $i;
    for( $i=0; $i<$v; $i++ )
    {
    polyCube;
    }
    {
    polyPlane -name "placementHelper";
    setAttr polyPlane1.subdivisionsWidth $v;
    setAttr polyPlane1.subdivisionsHeight $v;
    setAttr polyPlane1.width $scale;
    setAttr polyPlane1.height $scale;
    //todo: change the size and subdivs of the plane based on sliders for coverage area
    //and make the density value effect the amount of subdivs.
    }

    {

    select -r "placementHelper";
    delete;
    }
    }
    ////////////


    Now i'm trying to get the amount of verts from placementHelper mesh using polyEvaluate.


    I thought this would do it, but it doesn't work :/
    int $vertCount = `polyEvaluate -v';


    What i want to do is find out the amount of verts in the helper plane, and use that value as a maximum value to pick random numbers from. Those random numbers will be vertex ID's which get selected and a tree placed there. Or is there an easier way to randomly select verts?
  • haiddasalami
    Options
    Offline / Send Message
    haiddasalami polycounter lvl 14
    int $vertCount[] = `polyEvaluate -v $placementHelper`;
    

    polyEvaluate returns an array so you need to make the variable an array. Also watch the closing of tilda's. That's why there's a syntax error and on another note, dont assume the mesh will be selected always, I know you're making it in the script etc. This is more of a general good programming practice and is good to keep in a variable to access later, also helps when bug finding. Just store it with the ls -sl command in a variable and you can use it anywhere within the scope. Also on a side note, remember that the vertex count is 0 based.
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    Thanks for the quick reply. Unfortunately, i'm so clueless i still can't get it to work haha. This is what's happening:

    //
    global string {
    global string $placementHelper;
    int $vertexCount[] = `polyEvaluate -v $placementHelper`;
    print $vertexCount
    };
    // Error: {
    //
    // Error: Syntax error //
    // Error: int $vertexCount[] = `polyEvaluate -v $placementHelper`;
    //
    // Error: Invalid redeclaration of variable "$vertexCount" as a different type. //
    // Error: }; //
    // Error: Syntax error //






    I have no idea if a global string is the right way to use this either, just randomly stabbing in the dark at this and hoping things work ://
  • haiddasalami
    Options
    Offline / Send Message
    haiddasalami polycounter lvl 14
    Yeah whats happening is that its declared in the memory and wont get cleared till you restart maya. Definitely annoying and one of the reasons I hate Mel :D yeah and globals are definitely not the way to go especially i find with Mel. Mind posting the whole script so I can throw some edits/pointers?
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    Ah right, yes i do remember my teacher saying something about that issue with maya :/

    Here's the script thus far:
    global string $Density; 
    global string $Area;
     global string $placementHelper;
     //main window
     window -title "Giles Tree Gen V 0.1"
            -iconName "Tree Gen"
            -widthHeight 300 300;
     columnLayout -adjustableColumn true;
           
            
     $Density = `intSliderGrp -label "Density" 
                              -value 10
                              -fieldMinValue 0 
                              -fieldMaxValue 100.0
                              -field true`;
     $Area = `intSliderGrp -label "Area (Grid Units)" 
                           -value 10
                           -fieldMinValue 1
                           -fieldMaxValue 100.0
                           -field true`;
      columnLayout -adjustableColumn true;
            button -label "Random Place" -command "Randomplace()";
            button -label "Close" -command ("deleteUI -window " + $window);
            
       showWindow $window;       
      proc Randomplace()
      {
         global string $Density;
     int $v = `intSliderGrp -q -value $Density`;
         global string $Area;
     int $scale = `intSliderGrp -q -value $Area`;
     //placeholder for tree placement script, makes a cube.
         int $i;
         for( $i=0; $i<$v; $i++ )
         {
             polyCube;
         }
          {
             //placement helper
         }
             polyPlane -name "placementHelper";
                     setAttr polyPlane1.subdivisionsWidth $v;
                     setAttr polyPlane1.subdivisionsHeight $v;
                     setAttr polyPlane1.width $scale;
                     setAttr polyPlane1.height $scale;
            
         }
        {
            
         global string $placementHelper;
         int $vertexCount[] = `polyEvaluate -v $placementHelper`;
         //print placeholder until i have a function to use $vertexcount with
         print $vertexCount
        }
         {
             
     select -r "placementHelper";
     delete;
         }
     }
     
    
    
    
  • haiddasalami
    Options
    Offline / Send Message
    haiddasalami polycounter lvl 14
    So heres the code that I reworked to be more efficient in the long run. It should be easier to read and understand. Im gonna work through yours and comment it and hopefully you can look at what I have and see what I'm talking about.
    global proc gilesTreeGen() {
    	if (`window -exists gilesTreeGen`) 
            { 
                // if Window exists delete, so you cant call multiple windows
                deleteUI gilesTreeGen;
            }
    	
    	window -title "Giles Tree Gen V 0.1"
               -iconName "Tree Gen"
               -widthHeight 300 300 gilesTreeGen;
    	
    	columnLayout -adjustableColumn true;
    		
    		intSliderGrp -label "Density" 
                              -value 10
                              -fieldMinValue 0 
                              -fieldMaxValue 100.0
                              -field true density;
    		intSliderGrp -label "Area (Grid Units)" 
                           -value 10
                           -fieldMinValue 1
                           -fieldMaxValue 100.0
                           -field true area;
    	
    	columnLayout -adjustableColumn true;
            
    		button -label "Random Place" -command "randomPlace()" randomPlaceBTN;
            button -label "Close" -command ("deleteUI -window gilesTreeGen") closeWindowBTN;
            
      showWindow gilesTreeGen; 
    }
    
    global proc randomPlace(){
    	int $density =  `intSliderGrp -q -value density`;
    	int $scale = `intSliderGrp -q -value area`;
    	for( $i=0; $i<$scale; $i++ )
         {
             polyCube;
         }
    	string $placementHelper[] = `polyPlane -w $scale -h $scale -sx $density -sy $density -ax 0 1 0 -cuv 2 -ch 1`;
    	int $vertexCount[] = `polyEvaluate -v $placementHelper`;
    	print $vertexCount;
    	
    	//------ON DONE MOVING TREES------------//
    	delete $placementHelper;
    }
    
    gilesTreeGen();
    
    //So off the bat I removed the need for density and area as globals. You can actually name the window elements and they are global in a sense so you can query them in other procs.
    global string $Density; 
    global string $Area;
     global string $placementHelper;
    //So i wrapped the main window in its own function. Its generally good practice to keep windows and stuff in one function and way easier as you just have to call the function. So to run your script just type gilestreeGen();
     //main window
    //I gave the name a window again incase I need to reference it somewhere. Also incase the user runs the code again the script deletes the window and creates a new one. This is an example of where the name of the window came in handy
     window -title "Giles Tree Gen V 0.1"
            -iconName "Tree Gen"
            -widthHeight 300 300;
     columnLayout -adjustableColumn true;
           
     //Here i removed the global variables density and area and just named them density and area
     $Density = `intSliderGrp -label "Density" 
                              -value 10
                              -fieldMinValue 0 
                              -fieldMaxValue 100.0
                              -field true`;
     $Area = `intSliderGrp -label "Area (Grid Units)" 
                           -value 10
                           -fieldMinValue 1
                           -fieldMaxValue 100.0
                           -field true`;
      columnLayout -adjustableColumn true;
     //same deal with the buttons. Gave them names and BTN suffix for just further clarity.
            button -label "Random Place" -command "Randomplace()";
            button -label "Close" -command ("deleteUI -window " + $window);
     //just used window name here to show it 
       showWindow $window; 
       
      proc Randomplace()
      {
      //Theres no need to re-declare a global again (or atleast I dont, anywho I just queryed the density sildergroup and stored it in density variable. Same deal with scale
         global string $Density;
     int $v = `intSliderGrp -q -value $Density`;
         global string $Area;
     int $scale = `intSliderGrp -q -value $Area`;
     //placeholder for tree placement script, makes a cube.
     
    	 int $i;
         for( $i=0; $i<$v; $i++ )
         {
             polyCube;
         }
    	  {
             //placement helper
         }
    	 //You can actually edit the parameters of a polyplane before you create them and just assign w parameter with like scale variable. Plus one line of code :) Also stored in a variable for further use
             polyPlane -name "placementHelper";
                     setAttr polyPlane1.subdivisionsWidth $v;
                     setAttr polyPlane1.subdivisionsHeight $v;
                     setAttr polyPlane1.width $scale;
                     setAttr polyPlane1.height $scale;
            
         }
        {
         //removed need for this
         global string $placementHelper;
         int $vertexCount[] = `polyEvaluate -v $placementHelper`;
         //print placeholder until i have a function to use $vertexcount with
         print $vertexCount
        }
         {
     //You can also save a line and just delete something as long as you have the name without selecting it though this depends on what you want.        
     select -r "placementHelper";
     delete;
         }
     }
    

    Hopefully that works. Let me know if you want more clarification on stuff.
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    Wow, thanks so much for that!

    So by making a global proc, everything within it becomes global too, so to speak? How did i not figure that out... ha.

    That's made things a lot clearer and easier to understand. Glad that the vertex counter is working now too.

    I'l try to get a rand with a max value of the vertex count working next. After that i'm going to have to find out how to get the vertex positions and check them against another array (which i have no idea how to do either! hoorah!).
  • haiddasalami
    Options
    Offline / Send Message
    haiddasalami polycounter lvl 14
    well making it global doesnt make everything inside global. The global is there so you can call functions through the script editor. For something like that you need to look into arguments where you're basically passing data back and forth or doing setters and getters (cant remember if mel even supports it) Not sure where you're in right now for the class so maybe you're prof will explain this down the road.

    Dont worry too much about not knowing how to do it/it not working. Thats part of programming :) Just have fun and think of it as a problem to solve. if you get stuck somewhere just post here or google the terms/ error. Ill be gone later today but will check back tomorrow. keep at it :thumbup:
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    Right, straight away another issue haha. Google has no answer for this one either (lack of basic MEL stuff on the web is annoying).

    int $randomNumber1 = `rand $vertexCount[]`; print $randomNumber1;

    What i want to do here is take the vertex count and make it as the highest value the Rand can use (because obviously you don't want it trying to select a vertex ID that doesn't exist). But oh no, it won't work. It's coming up with invalid use of rand command. The script works fine when i replace it with int $randomNumber1 = `rand 100`; So obviously i'm referencing the $vertexCount wrongly :/


    This scripting lark is difficult!
  • haiddasalami
    Options
    Offline / Send Message
    haiddasalami polycounter lvl 14
    Sorry got sidetracked with something. Reason is $vertexCount[] is accessing nothing. You want to say `rand $vertexCount[0]`;
  • Denny
    Options
    Offline / Send Message
    Denny polycounter lvl 14
    Some notes about Maya and MEL.

    Like haiddasalami mentioned global proc means you can call the procedures directly but you do not get direct access to variables inside it. The variables will stay in their scope.

    If you define a variable in the MEL window those variables will become globals for the session. I noticed a nasty bug with Maya where I had the following code in a mel document;
    global proc addLast(string $objects[], string $entry){
       $objects[size($objects)] = $entry;
       return $objects;
    }
    


    In my MEL window I ran this code
    [B]string $objects[] = `ls -sl`;    // Get selection; Object AnotherObject |AThirdObject
    addLast($objects, "testentry");  // Add testentry to the end of the array
    print($objects);                 
    
    // Result: Object AnotherObject |AThirdObject testentry[/B]
    

    Note that I did not type $objects = addLast($objects, "testentry") in the MEL window, yet $objects in the MEL window stored the value of the procedure.

    MEL does not support direct overwrite access to variables like &variable in C++ or in out Variable in Ada, I have not managed to do it at least. What I believe happened here is that Maya "leaked" its global variable from the MEL window and confused it with the same variable name inside my procedure scope. So be careful with defining test variables in the MEL window, you might get weird results.




    If you're using globals and want to free space you can always use
    clear $variable;
    
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    Thanks for the info guys.

    Here's what i'm currently trying to do.
    string $placementHelper[] = `polyPlane -w $scale -h $scale -sx $density -sy $density -ax 0 1 0 -cuv 2 -ch 1`; 
        int $vertexCount[] = `polyEvaluate -v $placementHelper`;
         
         int $randomNumber1 = `rand $vertexCount[0]`;
         //print $randomNumber1;
         string $treePos = `pointPosition pPlane1.vtx[$randomNumber1]`;
         
         select -r pCube1;
         move -r $treePos;
         
    
    Yet again, it doesn't work and i can't figure out why. What should be happening is that a random number with the maximum value of $vertexCount[0] is being used to find a matching vertex ID and then use it's position to make $treePos, which is then used to move the cube to the position of that vert.

    The vertex counter is working, and so is the rand function. But the line
    string $treePos = `pointPosition pPlane1.vtx[$randomNumber1]`;
    
    Doesn't work and results in an error...

    I also need to work out a better way of moving the cube. obviously not all the cubes will have the same name and there is also 3 types of objects eventually. I need a way to move the previously added object... any ideas?
  • Denny
    Options
    Offline / Send Message
    Denny polycounter lvl 14
    I really recommend you read the error it gives. If you have code that gives you source errors, look for the specific part raising that error. If you have a runtime error it's recommended to add a print("--Some debug message begin--\n") and print("--Some debug message end--\n") in your code and place the prints so you can narrow down where it happens if no proper errors are thrown.
    string $treePos = `pointPosition pPlane1.vtx[$randomNumber1]`;
    // Error: Cannot convert data of type float[] to type string. //
    

    Here's the fixed code. :)
    string $placementHelper[] = `polyPlane -w $scale -h $scale -sx $density -sy $density -ax 0 1 0 -cuv 2 -ch 1`; 
        int $vertexCount[] = `polyEvaluate -v $placementHelper`;
         
         int $randomNumber1 = `rand $vertexCount[0]`;
         //print $randomNumber1;
         float $treePos[] = `pointPosition pPlane1.vtx[$randomNumber1]`; // You had a string here, you need a float array or vector to retrieve pos/rot/scale data
         
         select -r pCube1;
         move -a $treePos[0] $treePos[1] $treePos[2];
    
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    Ah right, so you have to store the coords in a float array and then seperately call out each number stored in it. Gotcha! Thanks a ton for the working code.

    That part works now. While i was having trouble with that i started on the percentage sliders. I'm having trouble getting this to work:
                 intSliderGrp -label "Oak Percentage"
                         -value 40
                         -fieldMinValue 0
                         -fieldMaxValue 100.0
                         -field true oak;
        //percentage slider calc
    
    int $oakPercent =  `intSliderGrp -q -value oak`;
    int $maxPine = 100 - $oakPercent;
    
            intSliderGrp -label "Pine Percentage"
                         -value 40
                         -fieldMinValue 0
                         -fieldMaxValue $maxPine
                         -field true Pine;
    
    

    So what i'm trying to do here is get the first slider and converting it into a percentage and using the remaining percentage as a maximum value for the next slider. I don't get any errors with this, but it's not effecting the max value either. I can just slide it up to 100 without any issues :/
  • Denny
    Options
    Offline / Send Message
    Denny polycounter lvl 14
    You're most likely only running that code once so it will only happen on first execution. I see a couple of errors, first you're using a float field max value while calling on an intSlider. If you're okay with integers then it doesn't matter, MEL is forgiving when converting some datatypes.

    Secondly you need to add a change / slide command to the slider you want affecting the other;

    // Read the help docs for the commands under intSlider
    -changeCommand(-cc)
    -dragCommand(-dc)
    intSliderGrp -label "Oak Percentage"
                         -value 40
                         -fieldMinValue 0
                         -fieldMaxValue 100.0
                         -dragCommand "yourCustomProcedure" // Can be a direct command too, procedures tend to be easier
                         -field true oak;
    

    I don't know if intSliderGroup is called like this but your customProcedure could be;
    global proc customProcedure(){
      int $oakPercent =  `intSliderGrp -q -value oak`;
      int $maxPine = 100 - $oakPercent;
      intSliderGroup -e -v Pine $maxPine;
    }
    
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    Another day another load of issues.

    I'm getting "Error while parsing argument" on the dragcommand proc. I thought i was doing it right, the script runs, but whenever you move the slider the error while parsing is spammed in the editor and the value doesn't get restricted to below the remaining percentage.
    global proc maxPinevalue(){     
     int $oakPercent =  `intSliderGrp -q -value oak`;
     int $maxPine = 100 - $oakPercent;
     intSliderGrp -e -v Pine $maxPine;
     }
      global proc gilesTreeGen() {
     	if (`window -exists gilesTreeGen`) 
             { 
                 // if Window exists delete, so you cant call multiple windows
                 deleteUI gilesTreeGen;
             }
     	
     	window -title "Giles Tree Gen V 0.1"
                -iconName "Tree Gen"
                -widthHeight 300 300 gilesTreeGen;
     	
     	columnLayout -adjustableColumn true;
     		
     		intSliderGrp -label "Density" 
                               -value 10
                               -fieldMinValue 0 
                               -fieldMaxValue 100.0
                               -field true density;
     		intSliderGrp -label "Area (Grid Units)" 
                            -value 10
                            -fieldMinValue 1
                            -fieldMaxValue 100.0
                            -field true area;
             intSliderGrp -label "Oak Percentage"
                          -value 0
                          -fieldMinValue 0
                          -fieldMaxValue 100.0
                          -field true oak;
         //percentage slider calc
               intSliderGrp -label "Pine Percentage"
                          -value 0
                          -fieldMinValue 0
                          -fieldMaxValue 100
                          -dragCommand "maxPinevalue()"
                          -field true Pine;
             intSliderGrp -label "Maple Percentage"
                          -value 0
                          -fieldMinValue 0
                          -fieldMaxValue 100.0
                          -field true Maple;
     	
     	columnLayout -adjustableColumn true;
             
     		button -label "Random Place" -command "randomPlace()" randomPlaceBTN;
             button -label "Close" -command ("deleteUI -window gilesTreeGen") closeWindowBTN;
             
     //percentage slider calc
               
       showWindow gilesTreeGen; 
     }
       gilesTreeGen();
    
    
  • MoP
    Options
    Offline / Send Message
    MoP polycounter lvl 18
    I might be way off here (it's been a while since I've been elbow-deep in MEL, as it were), but does referring to a control in that way actually work?
    int $oakPercent =  `intSliderGrp -q -value oak`;
    

    Here, you're querying an intSliderGrp and expecting the name "oak" to work. Where is it getting that value from? It's not a variable (no $ prefix), so is it a globally-accessible control name?

    All of those control names (Oak, Pine etc) seem like they should be handled differently so that you can actually reference the values to/from them properly.

    An easy way to check this would be to put a debug print inside your slider changeCommand proc to just print out the value that it's getting for $oakPercent ... I have a feeling that value is not coming back correctly.

    Then again, I might be way off and just lost familiarity with MEL, in which case, carry on :)
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    From what i gather, the value inside the field is being changed by the slider, and you can reference the value in the field by refereing to it's tag, ie Oak, Maple, Pine.
    $oakPercent is definately functioning as it should and returning the correct value, just the slider doesn't seem to want to work with it :/ I'm pretty much taking a stab in the dark at everything though, so i'm probably doing something drastically wrong here haha.
  • Denny
    Options
    Offline / Send Message
    Denny polycounter lvl 14
    Your error happens because you're having dragCommand on Pine, which is the same slider you're modifying. You want to have dragCommand on oak do you not? :)

    Currently:
    pineSlider -> dragCommand -> Modify Pine

    You want:
    oakSlider -> dragCommand -> Modify Pine
  • MoP
    Options
    Offline / Send Message
    MoP polycounter lvl 18
    Ah heh, well spotted. That's certainly it... feedback loop! :)
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    Derpderp! That certainly wasn't doing it any favours hahaha, but sadly hasn't solved it either! Still getting the same error. I'm not entirely sure what the drag command is meant to achieve though? Surely it's just change the pine slider to be a new value? Whereas i want to be able to set the maximum value, but the user still be able to choose any value between 0 and the maximum variable.

    Confused!
  • Denny
    Options
    Offline / Send Message
    Denny polycounter lvl 14
    dragCommand is the command that should be invoked when you drag the slider, just like it says.

    Oh, by the way, regarding names in MEL. If you type a word without quotation marks in certain contexts Maya will assume it's an object in the scene or a name. For example;

    select -r SomeObject;
    intSlider -min -100 -max 100 -v 0 -s 1 SomeName;


    Works just as well as

    select -r "SomeObject";
    intSlider -min -100 -max 100 -v 0 -s 1 "SomeName";


    I always do quotation marks in my scripts, it's a pet peeve of mine. I don't trust MEL. :)
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    I added back in the $maxPine to the max value of the pine slider, and now it's saying that maxPine is an undeclared variable, but surely it's being declared in the global proc maxPinevalue which comes before global proc gilesTreeGen?
    global proc maxPinevalue(){     
     int $oakPercent =  `intSliderGrp -q -value oak`;
     int $maxPine = 100 - $oakPercent;
     intSliderGrp -e -v Pine $maxPine;
     }
      global proc gilesTreeGen() {
         if (`window -exists gilesTreeGen`) 
             { 
                 // if Window exists delete, so you cant call multiple windows
                 deleteUI gilesTreeGen;
             }
         
         window -title "Giles Tree Gen V 0.1"
                -iconName "Tree Gen"
                -widthHeight 300 300 gilesTreeGen;
         
         columnLayout -adjustableColumn true;
             
             intSliderGrp -label "Density" 
                               -value 10
                               -fieldMinValue 0 
                               -fieldMaxValue 100.0
                               -field true density;
             intSliderGrp -label "Area (Grid Units)" 
                            -value 10
                            -fieldMinValue 1
                            -fieldMaxValue 100.0
                            -field true area;
             intSliderGrp -label "Oak Percentage"
                          -value 0
                          -fieldMinValue 0
                          -fieldMaxValue 100.0
                          -dragCommand "maxPinevalue()"
                          -field true oak;
         //percentage slider calc
               intSliderGrp -label "Pine Percentage"
                          -value 0
                          -fieldMinValue 0
                          -fieldMaxValue $maxPine
                          -field true Pine;
             intSliderGrp -label "Maple Percentage"
                          -value 0
                          -fieldMinValue 0
                          -fieldMaxValue 100.0
                          -field true Maple;
    
  • Denny
    Options
    Offline / Send Message
    Denny polycounter lvl 14
    Variables only exist within the same scope. Two procedures are two different scopes. Even if you made global variables this wouldn't work because the UI script will only run "once", which is the reason why you have commands like dragCommand so you can dynamically update the UI.

    In your intSlider definition you can only set the default value. Dynamically changing the value needs to be done with dragCommand or other external script.
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    Right, so how would one go about dynamically changing the value? Would something like setAttr work on an intSliderGrp? So have something like;
    global proc maxPinevalue(){ int $oakPercent = `intSliderGrp -q -value oak`;
    int $maxPine = 100 - $oakPercent;
    setAttr intSliderGrp -e -v "Pine" maxFieldValue $maxPine;
    }
  • Denny
    Options
    Offline / Send Message
    Denny polycounter lvl 14
    I found the problem, it seems the programmers at Autodesk has been inconsistent regarding syntax for intSlider.

    Original:
    intSliderGrp -e -v Pine $maxPine;
    

    New:
    intSliderGrp -e -v $maxPine Pine;
    

    It is standard to set the value last, like with setAttr, but not with intSlider. The code below is a working example.
    // dragCommandProcedure copies the current value to the target slider
    global proc dragCommandProcedure(){
       int $value = `intSlider -q -v intSliderSource`;
       intSlider -e -v $value intSliderTarget;
    }
    
    global proc UI_Procedure(){
       // ... Some code  ...
       intSlider -min -100 -max 100 -v 0 -s 1 -dc "dragCommandProcedure()" -parent $someParent intSliderSource;
       intSlider -min -100 -max 100 -v 0 -s 1 -parent $someParent intSliderTarget;
    }
    
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    Sneeky! As if things weren't confusing enough.

    Thanks a lot i'l try to implement it in a min.
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    Right it's all working now! However! In order to get the percentages to not go over 100 i need to be able to control the value of the other two sliders while one is being moved. I was hoping to be able to do something like this, but you can only have one -dragCommand per slider it seems, any ideas?

    intSliderGrp -label "Oak Percentage" -value 0
    -fieldMinValue 0
    -fieldMaxValue 100.0
    -dragCommand "maxPinevalue()"
    -dragCommand "maxMaplevalue()" //Cannot have this additional drag command
    -field true oak;
  • Denny
    Options
    Offline / Send Message
    Denny polycounter lvl 14
    You'll have to make a general procedure which in turn calls several other;
    global proc dragCommandProc(){
       maxPinevalue();
       maxMaplevalue();
    }
    
    global proc UI_Proc(){
       // Some code
       intSliderGrp -label "Oak Percentage" -value 0
          -fieldMinValue 0
          -fieldMaxValue 100.0
          -dragCommand "dragCommandProc()"
          -field true oak;
    }
    

    Alternative is:
    global proc UI_Proc(){
       // Some code
       intSliderGrp -label "Oak Percentage" -value 0
          -fieldMinValue 0
          -fieldMaxValue 100.0
          -dragCommand "maxPinevalue(); maxMaplevalue();"
          -field true oak;
    }
    

    Yes, you can use semicolon and type normal code in one long string. :)
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    Right, that hasn't quite worked how i imagined *sigh* Here is whats happening now.

    percent.jpg

    As you can see, the sliders clearly add up to more than 100. The sliderCommands are only adjusting themselves according to the previous value and not taking the next value into consideration. How should i go about getting all 3 values to not go over 100?
  • Denny
    Options
    Offline / Send Message
    Denny polycounter lvl 14
    I think something similar to this could work. It's mostly pseudo code but I hope you get the point.
    global proc normalizeSliders(string $slider){
       // $slider parameter is the current slider being acted upon
       string $sliders[] = {"density", "area", "oak", "Pine", Maple"};
       int $value = `intSliderGrp -q -v $slider`;
       
       // Use modulus and other calculations to divide the amount of overshoot you have on the current slider compared to the other values
       int $subtractValue = -- calculation here --;
    
       for($sl in $sliders){
          // Subtract the value
          int $v = `intSliderGrp -q -v $sl`;
          intSliderGrp -e -v ($v-$subtractValue) $sl;
       }
    }
    
    
    global proc UI_Proc(){
       intSliderGrp -dragCommand "normalizeSliders(\"oak\")" oak;
       intSliderGrp -dragCommand "normalizeSliders(\"Pine\")" Pine;
       intSliderGrp -dragCommand "normalizeSliders(\"Maple\")" Maple;
    }
    
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    Ahh ok. So i can store the slider values into an array and ref each one. Never thought of that. Little late here now but il try this out tommorrow.
  • Denny
    Options
    Offline / Send Message
    Denny polycounter lvl 14
    Storing the names in an array is a good way of being able to repeat the same action with a for loop instead of copy-paste the same action for each reference. :)
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    Okay, next issue!!!

    Cubes using this plane vert placement mode don't seem to want to place properly when using the code from the simple move x rand version.

    The cubes are all being placed in the correct amount, but ontop of each other and the script is failing at moving each cube as it gets created. Heres my code.. Where am i going wrong this time? #uselessatmel
    global proc randomPlace(){ 	int $density =  `intSliderGrp -q -value density`;
     	int $scale = `intSliderGrp -q -value area`;
     	int $testvalue = 20;
     	for( $i=0; $i<$testvalue; $i++ )
          
              polyCube -name ("treeA"+$i);
          print $i;
     	string $placementHelper[] = `polyPlane -w $scale -h $scale -sx $density -sy $density -ax 0 1 0 -cuv 2 -ch 1`;
     	int $vertexCount[] = `polyEvaluate -v $placementHelper`;
     	
     	int $randomNumber1 = `rand $vertexCount[0]`;
     	
     	
          float $treePos[] = `pointPosition pPlane1.vtx[$randomNumber1]`;      
          
          move ("treeA"+$i) -a $treePos[0] $treePos[1] $treePos[2];
        	//------ON DONE MOVING TREES------------//
     	delete $placementHelper;
     }
    
  • haiddasalami
    Options
    Offline / Send Message
    haiddasalami polycounter lvl 14
    K so the reason is you're first missing the open and close brackets for the for loop. Second the move command wants the object name at the end. Otherwise the script will just error out. Here's the fixed up code:
    for( $i=0; $i<$density; $i++ )
          {
              string $tree[] = `polyCube -name ("treeA"+$i)`;          
              int $randomNumber1 = `rand $vertexCount[0]`;
     	   //print $randomNumber1;
              float $treePos[] = `pointPosition pPlane1.vtx[$randomNumber1]`;      
              move -a $treePos[0] $treePos[1] $treePos[2] $tree[0];;
          }
    

    Also see how I am re-declaring the variable within the for loop. That ensures the variable is always fresh or not remembering the old trees. You might want to keep the order of the trees or something to iterate over with later on so you can store these in another array which you can then pass around to helper functions.
  • mccartm6
    Options
    Offline / Send Message
    Hey there im in the same class as Giles and doing the same project. I am a total novice when it comes to Mel. I was hoping maybe you guys could answer one or two questions i'm having.

    I'm going about this in a different manner to Giles and trying to keep it super simple.
    So far i have a script that generates randomly placed objects, the amount of each is controlled by a slider up to 100. It will also create even spacing for a more ordered "forest"

    polycount%20question3.JPG

    first question is how do i go about stopping objects overlapping i've looked everywhere and can't seem to figure it out?It's not the end of the world as trees grow close to each other in nature any way but if i could redcue it that would be great!

    polycount%20question.JPG


    the second issue is similar basically my ordered rows all start at 0 0 0 so how would i change the starting point so that they are offset. I know this is a simple one but im having a total mind block on it!


    polycount%20question2.JPG

    With regards the ordered dispersal is there any way to mix the trees so that they are not in individual blocks but are in a mixed orderly pattern?

    here's my code so far:


    string $window = `window -title "Forest Editor"
    -iconName "Short Name"
    -widthHeight 500 150
    -sizeable 0`;

    columnLayout -adjustableColumn true;

    intSliderGrp -label "Number Of Oak"
    -value 0
    -fieldMinValue 0
    -fieldMaxValue 100.0
    -field true Oak;
    intSliderGrp -label "Number Of Birch"
    -value 0
    -fieldMinValue 0
    -fieldMaxValue 100
    -field true Birch;
    intSliderGrp -label "Number Of Maple"
    -value 0
    -fieldMinValue 0
    -fieldMaxValue 100.0
    -field true Maple;



    button -label "Create Forest Random" -command "CreateForest";
    button -label "Create Forest Even" -command "CreateForestEven";
    button -label "Close" -command ("deleteUI -window " + $window);

    showWindow;


    global proc CreateForest(){
    int $count = `intSliderGrp -q -value Oak`;
    for( $i=0; $i<$count; $i++ )
    {
    polyCube
    -name ("myCube_"+$i);

    float $x = rand(-10, 10);
    float $y = rand(0, 0);
    float $z = rand(-10, 10);
    move $x 0.5 $z;
    }

    group;

    {
    int $count = `intSliderGrp -q -value Birch`;
    for( $i=0; $i<$count; $i++ )
    {
    polySphere
    -name ("mySphere_"+$i);
    float $x = rand(-10, 10);
    float $y = rand(0, 0);
    float $z = rand(-10, 10);
    move $x 0.5 $z;
    }

    group;
    }


    {
    int $count = `intSliderGrp -q -value Maple`;
    for( $i=0; $i<$count; $i++ )
    {
    polyCone
    -name ("myCone_"+$i);
    float $x = rand(-10, 10);
    float $y = rand(0, 0);
    float $z = rand(-10, 10);
    move $x 0.5 $z;
    }

    group;
    }
    }






    global proc CreateForestEven(){
    int $count = `intSliderGrp -q -value Oak`;
    for($rows=0; $rows<$count; $rows++)
    {
    for ($cols=0; $cols<$count; $cols++)
    {
    polyCube
    -name ("myCube_"+$rows);
    move -r ($cols*-2) 0.5 ($rows*2);

    }
    }

    group;

    {
    int $count = `intSliderGrp -q -value Birch`;
    for($rows=0; $rows<$count; $rows++)
    {
    for ($cols=0; $cols<$count; $cols++)
    {
    polySphere
    -name ("mySphere_"+$rows);
    move -r ($cols*2) 0.5 ($rows*-2);

    }
    }

    group;
    }


    {
    int $count = `intSliderGrp -q -value Maple`;
    for($rows=0; $rows<$count; $rows++)
    {
    for ($cols=0; $cols<$count; $cols++)
    {
    polyCone
    -name ("myCone_"+$rows);
    move -r ($cols*2) 0.5 ($rows*2);

    }
    }
    group;
    }
    }



    *DROPBOX images would load for some reason so here are the manual links
    http://dl.dropbox.com/u/44396673/scripting/polycount%20question3.JPG
    http://dl.dropbox.com/u/44396673/scripting/polycount%20question2.JPG
    http://dl.dropbox.com/u/44396673/scripting/polycount%20question3.JPG
  • Denny
    Options
    Offline / Send Message
    Denny polycounter lvl 14
    The easiest way to avoid overlapping is to do a distance check between the tree you're placing and all existing trees. If it's placed too close to another, remove it, or rather compare positions before placing the tree.

    Here's a code example on how to determine distance between two objects with vectors.
    // Get object positions
    vector $object1Pos = `xform -q -ws -translation $object1`;
    vector $object2Pos = `xform -q -ws -translation $object2`;
    
    // Get delta vector pointing from Object1 to Object2
    // Not relevant for distance but can be useful if you need the vector for other things
    vector $delta = ($object2Pos - $object1Pos);
    
    // Retrieve the magnitude of the vector, which is the same as distance when we're working with vector positions
    float $distance = `mag $delta`;
    

    Also use the [ code] [ /code] tags (without spaces) please. :)
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    Thread hyjackery!!! I joke i joke :P

    My scatter is finally working hoorah. My script is starting to come together now, however, i'm still having two issues.

    Sliders still don't bloody work, i am confused in the highest regard.

    My scatter works, but like Martin, i have to get the tree's to not place ontop of each other. Is there a way i can query a vertex and its neghbouring vertex's to find out if they have already been used? then it's just simply a matter of doing an if/else statement... right?
  • Denny
    Options
    Offline / Send Message
    Denny polycounter lvl 14
    If a vertex has been used? I don't follow. If you want to check if the trees are intersecting there are several methods but I'd say only the distance check is the easy one.

    1. Distance comparison between pivot
    2. Bounding Box comparison
    3. Convex polygon intersection (check triangle against triangle)
    http://gamemath.com/2011/09/detecting-whether-two-convex-polygons-overlap/
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    My script works by using vertex positions from the plane, so i was asking if there was a way to find out which vertex positions have been used from the plane already?
  • haiddasalami
    Options
    Offline / Send Message
    haiddasalami polycounter lvl 14
    My script works by using vertex positions from the plane, so i was asking if there was a way to find out which vertex positions have been used from the plane already?

    you could just store the vertex positions that you have used something like
    //OUTSIDE OF FOR LOOP//
    float $vertexList[];
    
    //Inside FOR LOOP//
    $vertexList[size($vertexList)] = pPlane1.vtx[$randomNumber1];
    

    Sucks that mel doesnt have push/pop or any array methods so this is a quick way to do a push.

    PS: I havent tested this, so there might be some errors but should work :poly136:
  • gilesruscoe
    Options
    Offline / Send Message
    gilesruscoe polycounter lvl 10
    So that stores the values that have been used and then what's going on inside the for loop? I don't quite understand :P
  • Denny
    Options
    Offline / Send Message
    Denny polycounter lvl 14
    In the loop you're placing trees, each time you place a tree you add it to the array. Next time you add a tree you're checking the array if it's already in there, if not place it. :)
1
Sign In or Register to comment.