Foolprof selections of material node, file node and SG node?

Become a member of the CGSociety

Connect, Share, and Learn with our Large Growing CG Art Community. It's Free!

THREAD CLOSED
 
Thread Tools Search this Thread Display Modes
Old 02 February 2013   #1
Foolprof selections of material node, file node and SG node?

I'm using this to fetch the material name of a face selection:
string $matName, $material[];
hyperShade -smn "";		
$material = `ls -sl`;
$matName = $material[0];

...which is rather simple. So I'm wondering if it's foolproof or if it can break somehow? I've tried it on a mesh with multiple materials and it seems to work just fine.


Next I want to get the file node from that material:
string $aidz[];
$aids = (`listConnections -destination false -source true $matName`);
select $aids[0];

...which is also rather simple and it seems to work everytime. But is it foolproof and can break?


And I also wanna be able to get the SG node:
string $lolk[];
$lolk = (`listConnections -destination true -source false ($matName + ".outColor")`);
print $lolk;

Does that seem okay to you?

The reason I ask is because I haven't done any testing with extensive node networks so there might be conditions I haven't thought of. For example, if you have a material with specular, normal map and whatever other maps you wanna have besides the color/diffuse - then the file node procedure above might fail. It appears though that the color/diffuse file node always appears first in the array but maybe I'm wrong?
__________________
Nightshade UV Editor (script for Maya) - Extends Maya's default UV Texture Editor with additional tools.

Last edited by DeadlyNightshade : 02 February 2013 at 01:22 PM.
 
Old 02 February 2013   #2
personally i would not use the "hyperShape" command. I prefer to manually trace the mesh to the materials.

When looking for files there may be nodes in between the file and the material - for example normal maps have a bump node in between them. In this case, you need to step over at least one node to find the file.

you're best bet is to write generic functions for retrieving data and don't make any assumptions about what you'll find. always test the result of each line to verify you found what you expected.

I had one of my tools fail because the outColor of a lambert material was plugging into the diffuse of another lambert!

You need to watch out when using the listConnections command - it will return the same node multiple times if the nodes are connected by multiple attributes, and it may return underworld nodes that are normally hidden from hyperShade/hyperGraph.

When a command returns a list, never assume that list will have 1 entry, and the thing you want is the first entry.

(in python, my mel is way to rusty to be trustworthy)
import maya.cmds as cmds
files = cmds.listConnections('lambert1', type='file', skipConversionNodes=True)
files = list(set(list)) # removes duplicates


Also, when looking for specific nodes, use the type flag. the outColor of a lambert could be connected to anything! you never know what artists will do....

shadingGroups = cmds.listConnections('lambert1', type='shadingEngine')
# lambert1 is special and is connected to two shading groups!
print shadingGroups
[u'initialShadingGroup', u'initialParticleSE']

Last edited by rgkovach123 : 02 February 2013 at 03:10 PM.
 
Old 02 February 2013   #3
Unless you can provide a solid argument for not using the hyperShade -command, I'll continue using it. It seems to work just fine.

If...
hyperShade -smn "";		
$material = `ls -sl`;
select $material;
...is flawed then I want to know what to replace it with. Those three lines of code is currently all that's needed to select the material node from a face selection. (previously I dug up the SG node, compared it to the face in a for-loop in order to find a match and long story short: too many lines of code).

Your listConnections -commands are similiar to mine. You use some smarter flags there though for filtering out crap. Mine are now:
listConnections -type "file" -skipConversionNodes true $theMaterial
listConnections -type "shadingEngine" -skipConversionNodes true $theMaterial

Either way I'll follow your advice regarding returned lists.

EDIT: The hyperShade -way of getting the material from a face selection seems to cause me some other troublems down the line.
__________________
Nightshade UV Editor (script for Maya) - Extends Maya's default UV Texture Editor with additional tools.

Last edited by DeadlyNightshade : 02 February 2013 at 04:32 PM.
 
Old 02 February 2013   #4
just make a new function called "getMaterialFrom" with an input argument. doesn't matter how many lines of code it is, because once its written, you can use it anywhere.

favoring hypershape over a custom function because "it's too many lines" is just a shortcut that can lead to problems in the future.

to get the material assigned to face in python...

def getMaterial(face):
	mesh = face.rsplit('.', 1)[0] # separates the string "pCube1.f[34]" into "pCube1" and "f[34]"
	sg = cmds.listConnections(mesh, type='shadingEngine')
	for s in sg:
		members = cmds.sets(s, query=True)
		if face in members:
			material = cmds.listConnections(s+'.surfaceShader')		  
			return material


this is just the bare bones, needs error checking. even though it shouldn't happen, polygons CAN belong to multiple materials...

edit: also when working with components, always use the Shape Node name.... Maya will happily use the format of Transform.Component, but the Sets command will return Shape.Component.

Therefore always convert stuff like "pCube1.f[1]" to "pCube1Shape.f[1]"
 
Old 02 February 2013   #5
I think I understand most of that Python code.
Face is fetched into a var, then processed to get rid of the crap in the string.
Connections are then listed for the face, according to shading engine. But you said that it's possible for a face to have two materials and that that can give errors - so that line would be a place for such an error. Several SG's might end up in the SG array when doing listConnections.

I know it's unlikely for a face to have multiple materials - I can only think of one scenario: splatmapping - but yea... that can still happen. I guess there's no way around that.

What I don't fully grasp is that final for-loop. It compares every fetched SG towards the SG on the face. So... mesh = face.rsplit (that row) just collects all faces of the mesh into an array? So it doesn't fetch "just" the selected face but all faces on the mesh?

Another thing: In the for loop you look for surfaceShaders. While that is good I wonder how you can make that search larger? Not all materials are surfaceShaders (all the Mental ray "mia_"-materials for example are not) which means that anyone working with procedual textures, rendering or whatever, will have problems with this function/procedure.
__________________
Nightshade UV Editor (script for Maya) - Extends Maya's default UV Texture Editor with additional tools.
 
Old 02 February 2013   #6
So I looked around a bit on other scripts to see how others dealt with this.
One such script is GetMat by Ken Proudfoot (written 10 years ago) - here's my version of his script:
// Get face info. Mode determines output: Shading Group node, Material node or File node.

global proc getFaceInfo(int $mode)
{
	string $buffer[], $faceSel[], $fileNode, $matNode, $SGNode, $SGs[], $temp;
	$fileNode = "";
	$matNode = ""; 
	$SGNode = "";
	  
	// Here I have a selection query (excluded in this example) that makes sure the user
	// has a valid face selection

	
	// Get all SGs in the scene
	string $SGs[] = `ls -type shadingEngine`;

	// Now match the scene SG's to the one on the selected face
	for ($obj in $SGs)
	{	
		// Is the shader found on our selected face(s)?
		if (`sets -isMember $obj $faceSel`)
		{
			$SGNode = $obj;
			break;
		}
	}
	
	// Get mat node from the SG node
	if ("shadingEngine" == `nodeType $SGNode` && `connectionInfo -isDestination ($SGNode + ".surfaceShader")`)
	{
		$temp = (`connectionInfo -sourceFromDestination ($SGNode + ".surfaceShader")`);
		tokenize $temp "." $buffer;	
		$matNode = $buffer[0];		
	}

	// Get file node from the material node
	if (`connectionInfo -isDestination ($matNode + ".color")`)
	{
		$temp = (`connectionInfo -sourceFromDestination ($matNode + ".color")`);
		tokenize $temp "." $buffer;
		$fileNode = $buffer[0];
	}
	
	// Output mode
	switch ($mode)
	{
		// Case 0 - Output material node
		case 0: 
			showEditorExact $matNode;			
			break;
	
		// Case 1 - Output file node
		case 1:			
			showEditorExact $fileNode;			
			break;
			
		// Case 2 - Output SG node			
		case 2:
			showEditorExact $SGNode;			
			break;
	}
}

getFaceInfo(0);

Thoughts?
(the above script is fully functional and works on a face selection)

EDIT: Now it looks for surfaceShaders so that is something I need to fix later on - in case the face selected has something like a mental ray material on it or similiar...
__________________
Nightshade UV Editor (script for Maya) - Extends Maya's default UV Texture Editor with additional tools.

Last edited by DeadlyNightshade : 02 February 2013 at 10:44 AM.
 
Old 02 February 2013   #7
What I don't fully grasp is that final for-loop. It compares every fetched SG
towards the SG on the face. So... mesh = face.rsplit (that row) just collects
all faces of the mesh into an array? So it doesn't fetch "just" the selected
face but all faces on the mesh?


the reason for splitting the string is so i can ask the mesh for its connections. basically:

take input string: "pCube1.f[45]"
convert string to "pCube1"
look for connections to shaders on: "pCube1"


Another thing: In the for loop you look for surfaceShaders. While that is good I
wonder how you can make that search larger? Not all materials are surfaceShaders
(all the Mental ray "mia_"-materials for example are not) which means that
anyone working with procedual textures, rendering or whatever, will have
problems with this function/procedure.


I am not looking for surfaceShader nodes, I am looking for what is connected to the surfaceShader attribute. The connection between a material node and a shadingEngine is usually ".outColor" > ".surfaceShader".

you can do something like:
materials = cmds.listConnections(sg+'.surfaceShader')
for m in materials:
	if cmds.getClassification(m) == 'shader/surface':
		print cmds.nodeType(m)


this will tell you what type of material is connected to the SG, whether it be a mentalRay material, a native maya material, a custom shader (provided it has been classified correctly by the author).
 
Old 02 February 2013   #8
Originally Posted by DeadlyNightshade: So I looked around a bit on other scripts to see how others dealt with this.
One such script is GetMat by Ken Proudfoot (written 10 years ago) - here's my version of his script:
// Get face info. Mode determines output: Shading Group node, Material node or File node.
 
global proc getFaceInfo(int $mode)
{
	string $buffer[], $faceSel[], $fileNode, $matNode, $SGNode, $SGs[], $temp;
	$fileNode = "";
	$matNode = ""; 
	$SGNode = "";
 
	// Here I have a selection query (excluded in this example) that makes sure the user
	// has a valid face selection
 
 
	// Get all SGs in the scene
	string $SGs[] = `ls -type shadingEngine`;
 
	// Now match the scene SG's to the one on the selected face
	for ($obj in $SGs)
	{	
		// Is the shader found on our selected face(s)?
		if (`sets -isMember $obj $faceSel`)
		{
			$SGNode = $obj;
			break;
		}
	}
 
	// Get mat node from the SG node
	if ("shadingEngine" == `nodeType $SGNode` && `connectionInfo -isDestination ($SGNode + ".surfaceShader")`)
	{
		$temp = (`connectionInfo -sourceFromDestination ($SGNode + ".surfaceShader")`);
		tokenize $temp "." $buffer;	
		$matNode = $buffer[0];		
	}
 
	// Get file node from the material node
	if (`connectionInfo -isDestination ($matNode + ".color")`)
	{
		$temp = (`connectionInfo -sourceFromDestination ($matNode + ".color")`);
		tokenize $temp "." $buffer;
		$fileNode = $buffer[0];
	}
 
	// Output mode
	switch ($mode)
	{
		// Case 0 - Output material node
		case 0: 
			showEditorExact $matNode;			
			break;
 
		// Case 1 - Output file node
		case 1:			
			showEditorExact $fileNode;			
			break;
 
		// Case 2 - Output SG node			
		case 2:
			showEditorExact $SGNode;			
			break;
	}
}
 
getFaceInfo(0);

Thoughts?
(the above script is fully functional and works on a face selection)

EDIT: Now it looks for surfaceShaders so that is something I need to fix later on - in case the face selected has something like a mental ray material on it or similiar...



If it works, use it! it is very hard to write fool-proof code, especially inside a program as big and buggy as maya. just be ready for it to break in 6 months because of some really random, weird configuration

the only change I would make is to NOT get all the SGs in the scene and loop through them... this can be slow on a big scene and if you call this script from within a larger process it could cause problems. Limit the number of SGs that are checked by first getting the SGs that are connected to mesh. This could take of the SGs to test from 100 down to 3 (again, depends on the complexity of the scene).

replace:
// Get all SGs in the scene
string $SGs[] = `ls -type shadingEngine`;

with:
// Get all SGs on the mesh
string $mesh[] = `ls -o $faceSel`;
string $SGs[] = `listConnections -type "shadingEngine" $mesh[0]`;
 
Old 02 February 2013   #9
Yea good idea, thanks!
I'll post an update on this script later today or tomorrow.
__________________
Nightshade UV Editor (script for Maya) - Extends Maya's default UV Texture Editor with additional tools.
 
Old 02 February 2013   #10
So this code is different because first I lost my train of thought. I went back to your comment about collecting all the SG's from the scene, to collecting all the SG's from the mesh - and I then ended up with "why not just collect all the SG's from the face???" and then I found the listSets command which I fancy a lot because it seems to be able to do just this. Either way it seems to work just fine.
		// Query selection type: face	
		string $faceSel[] = `filterExpand -ex true -sm 34`;  // Poly face -filter	
				
		if (!`size($faceSel)`) 	
		{
			confirmDialog 
				-button "Ok" 
				-cancelButton "Ok" 
				-defaultButton "Ok" 
				-dismissString "Ok" 
				-message "This feature only works on a face selection!" 
				-title "Error!";
				
			error("This feature only works on a face selection!");
		}
				
		// Now make sure only one face is selected
		$faceCount = `size($faceSel)`;

		if ($faceCount != 1)
		{
			confirmDialog 
				-button "Ok" 
				-cancelButton "Ok" 
				-defaultButton "Ok" 
				-dismissString "Ok" 
				-message "Please select only one face to sample from" 
				-title "Error!";
				
			error("Please select only one face to sample from");		
		}
		
		// Get face name. ls outputs as array...
		string $selFace[] = `ls -sl`;
		
		$SGs = `listSets -object $selFace[0]`;
...and now the var $SGs has the name of the first smoothing group. So after that I guess I need to make sure it's a surfaceShader and if not, move on to the next thing in the $SGs array...?

Some notes:
It will only work on a single face selection obviously - but that is intentional. I only want to sample the node from one face. And if there are no SG's then I'll just generate an error telling the user there are no SG's around on that face.

But... a face can have multiple SG's (from multiple materials - which would be the case when doing splatmapping) so what is the best way to go around that? Maybe select the first node - and then when running the script again, select the second node (and make it go around). Or is this a bad way of doing it? The idea is to give the user the ability to cycle between the nodes if there happens to be multiple ones on the face.
__________________
Nightshade UV Editor (script for Maya) - Extends Maya's default UV Texture Editor with additional tools.

Last edited by DeadlyNightshade : 02 February 2013 at 12:22 PM.
 
Old 02 February 2013   #11
materials are assigned through Sets and Render Sets are partitioned, so technically a single Face cannot belong to multiple Shading Groups. (Although i have encountered faces that do belong to multiple SGs, and the mesh flickers in the viewport when selecting and deselecting).

Remember, Materials are not connected to objects and faces directly... A material can be anything - even multiple materials - and the resulting color is fed to the Shading Group, which contains the objects and faces to draw with that appearance.
 
Old 02 February 2013   #12
Okay. Thanks for your feedback - you've been helpful!
__________________
Nightshade UV Editor (script for Maya) - Extends Maya's default UV Texture Editor with additional tools.
 
Old 02 February 2013   #13
one more thing to consider...

there are two types of sets - object set and render sets. object sets are used for things like quick selection sets, whereas render sets are used by materials.

the listSets command will return all the sets the face is a part of. so, it maybe possible for you to get sets back that are not of the type "shadingEngine". so be sure to test the results of the listSets command.
 
Old 02 February 2013   #14
Thread automatically closed

This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.
 
Thread Closed share thread



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
CGSociety
Society of Digital Artists
www.cgsociety.org

Powered by vBulletin
Copyright 2000 - 2006,
Jelsoft Enterprises Ltd.
Minimize Ads
Forum Jump
Miscellaneous

All times are GMT. The time now is 05:01 PM.


Powered by vBulletin
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.