GRID

Hi,

I want to create a grid based on subdivided poly plane. At each vertex should be placed one locator. The problem is – I cannot force this grid to update when I change subdivisionsWidth or subdivisionsHeight attributes of underlying plane. Don’t understand how to connect these attributes so that they would affect locator count and position.

Here's what I’ve got:

polyPlane  -axis 0 0 1 -height 10 -width 10 
 -subdivisionsHeight 10 -subdivisionsWidth 10;
int $VertCount[]= `polyEvaluate -vertex  pPlane1`;
 
int $i;
for ($i=0; $i < $VertCount[0]; $i=$i+1)
{
float $position[] = `xform -q -worldSpace -translation pPlane1.vtx[$i]`;
spaceLocator -position $position[0] $position[1] $position[2];
int $num=$i+1;
connectAttr -force pPlane1.vtx[$i] ("locator"+$num+".translate") ;
}

Don't understand how to connect these attributes so that they would affect locator count and position.

You can not really do this, well easily anyway. See maya for most parts assumes that the dg is static. That means that you cannot vary number of nodes based on a dg entry.

There exists 2 nodes that Are allowed to do this. However since your doing locators, particle instacer wont help you much. The othernode that could do this, does not have a suitable description node for doing this.

API time.

scriptJob with -attributeChange linked to the polyPlane node's subdiv and width/height attributes? ?

Actually, you can do it with expressions as well (easier to maintain/clean up than those pesky script jobs):

// Get current selection enabling running this script on a pre made polyPlane
 
//
 
string $sel[] = `ls -sl`;
 
string $plane[] = {};
 
string $polyPlane[] = {};
 
  
if (`size($sel)` > 0){
 
    if (`size($sel)` > 1){
 
        warning "only works with one object. Using first selection ONLY.\n";
 
    }
 
    // Determine if the selection is a plane and if so, find the polyPlane node
 
    //
 
    string $shapes[] = `listRelatives -s $sel[0]`; 
 
    if ( nodeType($shapes[0]) == "mesh"){
 
        $polyPlane = `listConnections -t "polyPlane" $shapes[0]`;
 
        // If it didn't find a polyPlane node (if the selected plane's history had been deleted)
 
        //
 
        if (!`size($polyPlane)`){
 
            print "No polyPlane node was found. Creating a new plane.\n";
 
            $plane = `polyPlane  -axis 0 0 1 -height 10 -width 10 -subdivisionsHeight 10 -subdivisionsWidth 10 -n "locCtrlPlane"`;
 
            $polyPlane[0] = $plane[1];
 
        }else{
 
            // polyPlane node was found which means that $sel[0] is a plane
 
            //
 
            $plane[0] = $sel[0];
 
        }
 
    }
 
}else{
 
    $plane = `polyPlane  -axis 0 0 1 -height 10 -width 10 -subdivisionsHeight 10 -subdivisionsWidth 10 -n "locCtrlPlane"`;
 
    $polyPlane[0] = $plane[1];
 
}    
 
int $VertCount[]= `polyEvaluate -vertex $plane[0]`;
 
string $group = `group -em -n "locGroup"`;
 
for ( $i=0; $i < $VertCount[0]; $i=$i+1){
 
    string $loc[] = `spaceLocator -position 0 0 0`;
 
     string $expr = `createNode expression -n ($loc[0] + "_posExpr")`; 
 
    expression -e -s ("float $pos[] =`xform -q -ws -t " + $plane[0] + ".vtx[" + $i + "]`;\n" + $loc[0] + ".translateX = $pos[0];\n" + $loc[0] + ".translateY = $pos[1];\n" + $loc[0] + ".translateZ = $pos[2];")  -o $loc[0] -ae 1 -uc all $expr; ;
 
 
 
    parent -r $loc[0] $group;
 
}

Oh, forgot all about the updating issue.. haha.

At least, now you can push/pull/deform your plane and the locators will follow (requires a scrub for the expressions to evaluate, though..)

And the locators are all under a group called locGroup, so the scriptJobs can delete that group (including the locators) and re-run the locator generation script as a procedure in the script Job every time the creation attributes of the plane changes....

I'll come up with something...hehe

Also, now we have 1 expression for every locator, which makes it a total mess.

We can gather up all the locators in an array, and then, after creation, we can make ONE expression controlling all locators.

This works..
It basicly does the same thing. But it now creates all the locators and then creates ONE expression controlling them all.
And it creates a scriptNode holding the updating procedures and the scriptJob declarations (so they automatically get initialized when reopening the scene.

// Get current selection enabling running this script on a pre made polyPlane
//
string $sel[] = `ls -sl`;
string $plane[] = {};
string $polyPlane[] = {};
string $allLocs[] = {};
string $allExpressions[] = {};
 
// If nothing is selected, create a plane
//
if (`size($sel)` > 0){
    if (`size($sel)` > 1){
        warning "only works with one object. Using first selection ONLY.\n";
    }
 
    // Determine if the selection is a plane and if so, find the polyPlane node
    //
    string $shapes[] = `listRelatives -s $sel[0]`;
    if ( nodeType($shapes[0]) == "mesh"){
        $polyPlane = `listConnections -t "polyPlane" $shapes[0]`;
 
        // If it didn't find a polyPlane node (if the selected plane's history had been deleted)
        //
        if (!`size($polyPlane)`){
            print "No polyPlane node was found. Creating a new plane.\n";
            $plane = `polyPlane  -axis 0 0 1 -height 10 -width 10 -subdivisionsHeight 10 -subdivisionsWidth 10 -n "locCtrlPlane"`;
            $polyPlane[0] = $plane[1];
        }else{
            // polyPlane node was found which means that $sel[0] is a plane
            //
            $plane[0] = $sel[0];
        }
    }
}else{
    $plane = `polyPlane  -axis 0 0 1 -height 10 -width 10 -subdivisionsHeight 10 -subdivisionsWidth 10 -n "locCtrlPlane"`;
    $polyPlane[0] = $plane[1];
}    
 
int $VertCount[]= `polyEvaluate -vertex $plane[0]`;
 
string $group = `group -em -n "locGroup"`;
 
for ( $i=0; $i < $VertCount[0]; $i++){
    string $loc[] = `spaceLocator -position 0 0 0`;
    parent -r $loc[0] $group;
    $allLocs[size($allLocs)] = $loc[0];
}
 
string $expr = `createNode expression -n "locPosUpdateExpr"`;
string $exprValue = "";
int $i=0;
for ($locator in $allLocs){
    $exprValue = $exprValue + ("float $pos[] =`xform -q -ws -t " + $plane[0] + ".vtx[" + $i + "]`;\n" + $locator + ".translateX = $pos[0];\n" + $locator + ".translateY = $pos[1];\n" + $locator + ".translateZ = $pos[2];\n\n")  ;
    $i++;
}
expression -e -s $exprValue -o $group -ae 1 -uc all $expr;
 
string $scriptNode = `scriptNode  -beforeScript "global proc string getLocatorUpdateProcScriptNode(){\n\tstring $scripts[] = `ls -type \"script\"`;\n\tfor ($script in $scripts) {\n\t\tif (`attributeQuery -node $script -ex \"group\"`) {\n\t\t\treturn $script;\n\t\t}\n\t}\n}\n\nglobal proc updateLocCount(){\n\tstring $script = `getLocatorUpdateProcScriptNode`;\n\tstring $polyPlane[] = `listConnections ($script + \".polyPlane\")`;\n\tstring $plane[] = `listConnections ($script + \".plane\")`;\n\tstring $groups[] = `listConnections ($script + \".group\")`;\n\tstring $exprs[] = `listConnections ($script + \".expr\")`;\n\n\t// Cleanup: delete the old locators and expression\n\t//\n\tselect -r $groups[0];\n\tdoDelete;\n\tselect -r $exprs[0];\n\tdoDelete;\n\t\n\t//Now, create new stuff\n\t//\n\tstring $allLocs[] = {};\n\tstring $group = `group -em -n \"locGroup\"`;\n\tint $VertCount[]= `polyEvaluate -vertex $plane[0]`;\n\n\tfor ( $i=0; $i < $VertCount[0]; $i=$i+1){\n\t\tstring $loc[] = `spaceLocator -position 0 0 0`;\n\t\n\t\tparent -r $loc[0] $group;\n\t\t\n\t\t$allLocs[size($allLocs)] = $loc[0];\n\t}\n\t\n\t$expr = `createNode expression -n \"locPosUpdateExpr\"`; \n\tstring $exprValue = \"\";\n\tint $vtx = 0;\n\tfor ( $locator in $allLocs){\n\t\t$exprValue = $exprValue + (\"float $pos[] =`xform -q -ws -t \" + $plane[0] + \".vtx[\" + $vtx + \"]`;\\n\" + $locator + \".translateX = $pos[0];\\n\" + $locator + \".translateY = $pos[1];\\n\" + $locator + \".translateZ = $pos[2];\\n\\n\")  ;\n\t\t$vtx++;\n\t}\n\texpression -e -s $exprValue -o $group -ae 1 -uc all $expr;\n\t\n\tconnectAttr -f ($group + \".message\") ($script + \".group\");\n\tconnectAttr -f ($expr + \".message\") ($script + \".expr\");\n}\n\nglobal int $jobNum = 0;\n$jobNum = `scriptJob -ac ($polyPlane[0] + \".subdivisionsWidth\") \"updateLocCount\"`;\n$jobNum = `scriptJob -ac ($polyPlane[0] + \".subdivisionsHeight\") \"updateLocCount\"`;" -st 1 -n "locatorUpdateProcScriptNode"`;
 
select -r $scriptNode ;
addAttr -ln "group" -at "message" ;
addAttr -ln "polyPlane" -at "message" ;
addAttr -ln "plane" -at "message" ;
addAttr -ln "expr" -at "message" ;
 
connectAttr -f ($group + ".message") ($scriptNode + ".group");
connectAttr -f ($polyPlane[0] + ".message") ($scriptNode + ".polyPlane");
connectAttr -f ($plane[0] + ".message") ($scriptNode + ".plane");
connectAttr -f ($expr + ".message") ($scriptNode + ".expr");
 
scriptNode -eb $scriptNode;
select -r $plane[0];

Yes, it kindof works, except offcourse it does not scale in production*. The reason you would use the api is that tracking the nodes in mel is allmost a impossible task. In any case your breaking the dg logic and run a risk of detaching parts of the expression driven locators permanently. See it has 3 main problems:

  1. it may be dangerously ustable to instanciate this node, either by copying or by reference, reference works ONLY if your using namespaces and one namespace only.

  2. you may break the rig by renaming the central nodes, as theres no formal connection between the expression and the plane. (this does happen so LOCK the nodes or youll have auser who by mistake breaks the entire thing)

  3. Depending on this to actually sucessfully render in batch mode IS stretching it to say the least (assuming on replaces the locator with something else), because mentalray will have a absolute fit with its incremental export, Anyway if you really want geometry then use particle instancing its also about 100 times faster to execute. (this ones not easy to solve in the api either)

MAIN REASON TOUGH. Its much harder to maintain than the corresponding api node. Its true thet API development for most parts is harder but the api code for THIS one is actually much more straightforward than the expression. because in essence the api is a special language for making nodes. PLUS the api node actually makes sense in the long run as it does scale also in applicability.**

API time

PS: Scriptjobs dont work, because they dont update during dg evaluation.

PPS: you can make the expression more efficient and easier to manage if you do not use the getAttr -ws -t part. By about 20 times more efficient as a matter of fact.

  • there is ofcurse no need to react on this if a hack is enough it is enough. Just saying its not necceserlyi a good leraning experince if your aim is not to fail.

** if someone is interested i have the underpinnings for such a node avilable and can share IF someone reminds me in the afternoon or other such suitable time.

Guys, thank you for replies and scripts…

Didn’t think, that such a simple task, would lead us to such an extended code (involving expressions) – without needed result – at subdivision 20 the grid updates very slowly (almost impossible to sketch, if you don’t know the grid size beforehand)

Still don’t understand why locator count cannot somehow follow the subdivisions amount. What is it mel for in this case? Create something without an option to adjust it of follow some simple rules?

API – is the best solution for tasks like this?

Thanks for your feedback, Joojaa.
I wholeheartedly agree for the most part. You raise some very valid points.
I do think, though, that in some cases, lets say I need the vertices for baking out some position locators for export to Nuke or whatever, EVEN in a large production this will yield much better results (as a one or five time use thing).
I could now, after a bit of scripting, have used it for what I needed and published the position locators to whoever requested them (or do something else with them, bake whatever result, it being particles, influences geometry or whatever).
My point is, most people aren't technical artists or TDs and often, if you know a bit of mel (or in most cases even better; Python) you can get things done without passing it onto the TDs queue of things to do, waiting for it to be ready and then go back the shot (because you had to do something else while waiting) bla bla bla..
If you want to get something done.....

However, I too think that particle instancing would probably be a much better way to do something like this.

I disagree with a couple of points, though:
ScriptJobs DO work. In this case we needed the scriptJobs to update the locators when changing the subdivision. I'm not going to change that during dg evaluation anyway, so that point isn't really relevant.

Which nodes would you rename in order to break it?
The scriptNode preserves the connections to the central nodes, so tahat shouldn't be a problem.
You are correct about deleting or renaming the plane, but thats easy to fix. Just add a dummy float attr to the plane and have the expression do something with it.
Most problems like that shouldn't matter (and if you're DELETING the plane it probably means you don't need the locators either, so get rid of it all).

Yes it isn't fool proof and not very flexible, but when you have a task at hand, you usually try and do the least amount of work (unless you are actually writing this as a general script and plan on releasing it). Doing otherwise would be like a modeler modeling the whole house, no wait, the whole street in case we need to bring the camera out of the living room (where the shot is).
I'm speaking as an artist who use mel to get things done and also set up shot- or project specific tools and not as a software/pipeline engineer.
I DO get what you are saying (and you also point out that "if a hack is enough..", and that's what I think might be  enough in this case (depending on the further usage of the setup).

By the way, what did you mean about ONE namespace and ONE namespace only?

yes, hehe this is where Joojaa is right (as usual). Scriptjobs are veery slow and expressions also.
So anything higher resolution definately points towards an API solution (which could probably be made to be interactive/realtime.
Just have to find someone who knows the API and C++/Python...

We would actually need to know what the actual need is to device a efficient startegy I think. Thing is if the data is actually designed for export and baking. Then making the data update on demand is a bit pointless. See there has to be some reason INSIDE the users working environment that needs the update for this to be a viable option. And in this case the scriptJob IS the tool to use only the scribjob should be called by the save process not by users update. Thisway you can use the MUCH more streamlined code of the original and undo aftwerwards. Ofcourse even that is a clunky option IF you export amations. Thing is a lot of effort is being spent on the needs to update in dg part of the equation. And theres really no need in those instaces its a nice feature sure. But is it worth paying for each frame?

you can get things done without passing it onto the TDs queue of things to do

And thats not what i say, theres allways a place for ugly solutions somewhere. However I am talking about good use of mel, and yoru resources here on the forum. So its not the kind of thing you should explore as your first option if theres really no pressing need. And your right doing the code also helps the TD's if and whan you really need to scale it up later, atleast its a sane process.

I will  allways tell people to avoid changing the dg in runtime with mel. Its possible sometimes even a good idea but....

at subdivision 20 the grid updates very slowly

yes well a grid expands exponentially so it gets slow very fast at some point anyway

which could probably be made to be interactive/realtime.

Well... perhaps. However dg creation IS SLOW theres really no need for a api solution to resort into doing that it can just kmake it all happen inside a single node and be done with it. The greatest benefit is that you can actually defeer the entire complexity till you need it (export render etc) and use a lighter proxy untill you do. This is MUCH faster.

PS: developping anything but quick hacks requires you to view the problem in longer perspective. And that makes my vantage point different from yours.

PPS: and no python is not better for those hacks, its more effecient true. But mainly more effeicent in shooting you in your own leg if not used properly with aftertought. There is a wondefull example of this in mayas core scripts made at autodesk, its 2000 lines of python code that a mel user would have been forced to do with 4 lines of mel and a lot of critical thinking. Now the python user COULD have done it with 4 python calls too.n But instead of investigating things they did what came to their mind at the first second.

PPPS: No im not allways right.

Still don’t understand why locator count cannot somehow follow the subdivisions amount. What is it mel for in this case?

Ok this deserves a bit of explanation. Mel does not operate on a keep executing this level. nodes do.

Nodes are like cars they enable you to drive form one point to another containg some cargo. The cargo is your end result and mel is your order form to the factory. Mel is a instruction on how to assmeble your car fleet.

So mel tells maya that it needs so and so nodes to execute so and so. However the mel exeutes its orders ONE TIME ONLY. its like shipping orders to yoru car dealer, once he has shipped you the 5 cars thats it. They didnt ship you cars indefintely.

Now what you want is a car that actually makes more cars and scraps them on demand. So you need a node. Expressions are more or less meant for you to drive arbitrary code, altough you want to avoid that. So you can actually make a expression that executes mel that does this. But ist hugely ineficient because maya needs to constantly be shipping new orders of nodes. And mayas Explicitly designed NOT TO DO THIS, the entire performance of maya comes from the idea that NODES do the work, and that the node netwroks involved are static, so theres no creation or destruction isndie maya druing playback.

Now this problem arises because you very specifically ASK for locators, if you would have asked boxes polygons of prettymuch anything except lightsources and locators ther would have been no problems. Because we could have instricted maya to combine many things into ONE NODE.

What is it mel for in this case?

So to aswer that quetion more precicely. Mel is the reciepie and nodes are the kitchen.

Ooh…

Look at this example

http://web.mac.com/rhino3dtv/GH/GH\_05\_08\_Demo\_Series.html

So simple, flexible and interactive!

Is there any way to achieve the same workflow inside maya? How?

I have a very rough idea about API, but as I understand I can create my personal node with attributes which will perform the same function as this “series” in video. Or another logic is should be used?

well can not look at the video right now . But yes you can make maya replicate stuff freely. You just asked a very spesific thing that includes 2 separate ideas at once. See locator is a node. You can not replicate the node easily just the data. Had you asked sphere then our aswer would have been different. The locator is just dataless