PDA

View Full Version : Assign FaceIDs to custom attribute


WesMcDermott
11-16-2012, 02:21 AM
Hi,

How can I add the faceIDs from the material assignment of an SG Node to a custom attribute on a shape node? I'm not sure where to start. I want to loop through the shapeNodes in my scene, add an custom attribute and add the faceId's from the material.

Thanks,

Wes

zaskar
11-16-2012, 07:02 AM
You can create an array attribute at your mesh and store the ids. To get the ids: the material assignment at component level is made using compound connections, so there is no way to directly get the ids using getAttr etc. The information connections are shape.instObjGroups[x].objectGroups[y]->SG.dagSetMembers[z]. The appendix ".objectGroups[y]" only is present if materials are assigned at component level, whole shapes are connected with only ".instObjGroups[x]". Where x stays for the instance of this shape and should be zero in most of the cases. The y in ".objectGroups[y]" stays for a distinct connection of all objects components to a certain shadingEngine. Now you could query ALL connected components inside a certain shading engine with the "sets -q nameOfShadingEngine" but then you inevitably have to loop through the result with tokenizing and sub loops to compare the shape names, so its better to create a helper node, an objectSet, and duplicate the connection of a shape into a certain SG into that object set and then qery the members of that set with the "sets -q" command. You then have the full component list of only this one shape and then you can loop through that list and tokenize out the face ids. Heres a quick code for extracting ids from component lists and assigning to array attributes: string $tmp[];
int $counter = 0;
string $compList[] = {"mesh.f[0:9]","mesh.f[1234:4321]","mesh.f[4323:9999]"};
string $compIdArray[] = {};
for($comp in $compList){
int $numToken = `tokenize $comp "[:]" $tmp`;
for( $i=int($tmp[1]); $i<=int($tmp[$numToken - 1]); $i++){
$compIdArray[$counter] = $i;
$counter++;
};
};
eval("setAttr mesh.idList -type stringArray " + $counter + " " + `stringArrayToString $compIdArray " "`);
where ".idList" is the array attribute of your shape you created earlier.

PS: if you want to store the face ids to work on them later its maybe faster to store the ids compressed, means instead of every single id store them as 0:9, 1234:4321, 4323:9999. Should speed up your script if working with dense meshes.

WesMcDermott
11-16-2012, 01:16 PM
You can create an array attribute at your mesh and store the ids. To get the ids: the material assignment at component level is made using compound connections, so there is no way to directly get the ids using getAttr etc. The information connections are shape.instObjGroups[x].objectGroups[y]->SG.dagSetMembers[z]. The appendix ".objectGroups[y]" only is present if materials are assigned at component level, whole shapes are connected with only ".instObjGroups[x]". Where x stays for the instance of this shape and should be zero in most of the cases. The y in ".objectGroups[y]" stays for a distinct connection of all objects components to a certain shadingEngine. Now you could query ALL connected components inside a certain shading engine with the "sets -q nameOfShadingEngine" but then you inevitably have to loop through the result with tokenizing and sub loops to compare the shape names, so its better to create a helper node, an objectSet, and duplicate the connection of a shape into a certain SG into that object set and then qery the members of that set with the "sets -q" command. You then have the full component list of only this one shape and then you can loop through that list and tokenize out the face ids. Heres a quick code for extracting ids from component lists and assigning to array attributes: string $tmp[];
int $counter = 0;
string $compList[] = {"mesh.f[0:9]","mesh.f[1234:4321]","mesh.f[4323:9999]"};
string $compIdArray[] = {};
for($comp in $compList){
int $numToken = `tokenize $comp "[:]" $tmp`;
for( $i=int($tmp[1]); $i<=int($tmp[$numToken - 1]); $i++){
$compIdArray[$counter] = $i;
$counter++;
};
};
eval("setAttr mesh.idList -type stringArray " + $counter + " " + `stringArrayToString $compIdArray " "`);
where ".idList" is the array attribute of your shape you created earlier.

PS: if you want to store the face ids to work on them later its maybe faster to store the ids compressed, means instead of every single id store them as 0:9, 1234:4321, 4323:9999. Should speed up your script if working with dense meshes.


Hi zaskar,

Thanks very much for the help! I really appreciate you taking time to write this code. I can't seem to figure out how to create the "idList" custom attribute array on the shapeNode. When adding a custom attribute, I am only seeing float, vector, integer, string, bool and enum. How can I create a custom attribute of array type?

Thanks again!

Wes

WesMcDermott
11-16-2012, 01:37 PM
Hi Zaskar,

I figured out how to add the custom attribute string array data type.

string $objects[] = `ls -sl`;
string $_material[];
addAttr -ln "_material" -sn "_material" -dt "stringArray" $objects;

Cheers,

Wes

WesMcDermott
11-16-2012, 06:53 PM
Well, messing with the code a bit further and I can't seem to get the correct values I need. I need to store faceIDs as an int array. I can't seem to get this working as I can only add a string array.

addAttr -ln "FACESET_material_box" -sn "FACESET_material_box" -dt "stringArray" $objects;

The data I am trying to store is the faces associated to the shading Group. In the relationship editor it is as follows...

lambertSG
-pCube1.f[0]
-pCube1.f[1]
-pCube1.f[2]
-pCube1.f[3]
-pCube1.f[4]
-pCube1.f[5]

When I output the faces stored using the code posted by zaskar, I get the following....

string $da[] = `getAttr pCubeShape1.FACESET_material_box`;
// Result: 0 1 2 3 4 5 6 7 8 9 1234 1235 1236 1237 1238 1239 ----- to ----- 9999 //

I have another file that has the faceID's stored in a custom attribute as an int array and it outputs the following which is correct....

int $di[] = `getAttr MeshShape.FACESET_material_Box`;
// Result: 0 1 2 3 4 //

zaskar
11-16-2012, 10:16 PM
Seems that you skipped all my explanations how to get the face ids. The line string $compList[] = {"mesh.f[0:9]","mesh.f[1234:4321]","mesh.f[4323:9999]"}just was an example how the resulting face list would look like if you did what i suggested and further how to deal with it. Anyway, here is the full code of how it was meant. Select a mesh with shaders assigned at component level, then run the code. The result is an integer array attribute per assigned shading engine added to the shape node, containing the affiliated face ids. No comments, you need to dive into it and understand it by yourself. {
string $sel[] = `ls -dag -ni -type "mesh" -sl`;
for($meshShape in $sel){
string $multiAttrList[] = `listAttr -multi ($meshShape + ".instObjGroups[0].objectGroups")`;
if(! `size $multiAttrList`){
print($meshShape + ": no shader assigned at component level, skipping\n");
continue;
};
string $objGroupIds_str[] = {};
for($attr in $multiAttrList){
string $tmp[];
tokenize $attr "[]" $tmp;
$objGroupIds_str[`size $objGroupIds_str`] = $tmp[3];
};
$objGroupIds_str = `stringArrayRemoveDuplicates $objGroupIds_str`;
int $shaderConnections = `size $objGroupIds_str`;

int $groupIds[] = {};
for($i=0;$i<$shaderConnections;$i++)
$groupIds[$i] = int($objGroupIds_str[$i]);

string $objSet = `createNode objectSet`;
for($i=0;$i<$shaderConnections;$i++){
string $connections_to_SG[] = `listConnections -plugs 0 -source 0 -destination 1 -type "shadingEngine" ($meshShape + ".instObjGroups[0].objectGroups[" + $groupIds[$i] + "]")`;
connectAttr -f ($meshShape + ".instObjGroups[0].objectGroups[" + $groupIds[$i] + "]") ($objSet + ".dagSetMembers[0]");
print($connections_to_SG[0] + " " + ($meshShape + ".instObjGroups[0].objectGroups[" + $groupIds[$i] + "]") + "\n");
string $componentList[] = `sets -q $objSet`;

string $tmp[];
string $compIds[] = {};
int $compCounter = 0;
for($component in $componentList){
int $numToken = `tokenize $component "[:]" $tmp`;
for($k=int($tmp[1]); $k<=int($tmp[$numToken - 1]); $k++)
$compIds[$compCounter++] = $k;
};

string $newAttrName = "FACESET_" + $connections_to_SG[0];
if(! `attributeExists $newAttrName $meshShape`)
addAttr -ln $newAttrName -sn $newAttrName -dt Int32Array $meshShape;

eval("setAttr " + $meshShape + "." + $newAttrName + " -type Int32Array " + $compCounter + " " + `stringArrayToString $compIds " "`);
};
delete $objSet;
};
};

WesMcDermott
11-17-2012, 12:17 AM
Seems that you skipped all my explanations how to get the face ids. The line string $compList[] = {"mesh.f[0:9]","mesh.f[1234:4321]","mesh.f[4323:9999]"}just was an example how the resulting face list would look like if you did what i suggested and further how to deal with it. Anyway, here is the full code of how it was meant. Select a mesh with shaders assigned at component level, then run the code. The result is an integer array attribute per assigned shading engine added to the shape node, containing the affiliated face ids. No comments, you need to dive into it and understand it by yourself.

Hi zaskar,

Thanks very much for the help and clarification. Unfortunately, I didn't understand your first explanation of the method for the face IDs. I appreciate you taking time to write this script and its really helped me to understand the objectSet node structure. I've been reading the docs all day as I'm really wanting to figure this out on my own, I just needed some direction.

Thanks much!

Wes

CGTalk Moderation
11-17-2012, 12:17 AM
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.