PowerPose Templates generator script
Hi, once again I started a script project to solve a specific issue I had. Read How to Get PowerPose to Work with Genesis 3?
I want to create a simple PowerPose template but I'm too lazy to write all the boring stuff myself even with copy-paste this takes some time and is prone to typos. So yesterday I started to write this script that generates all these lines for me that look like HTML tags based on the selected figure to have at least something to work with while creating the actual tamplate.
For me it is meant to be used to create a face rig template for genesis 3 figures and there are 64 control points in the template that need to get defined for PowerPose with a bone label and a position on the pane.
Here is my first version that does at least something usable. I can imagine this script could also serve others that want to create PowerPose templates for their custom figures. Maybe I should add some kind exclude bones check so you can run it from the root node of the figure without having everything included.
It generated 640 lines of PowerPose template code in a few milliseconds and includes all control points that I wanted for my PowerPose template.
It is far from beeing perfect and I need to find some other way to positioning the control points currently that isn't working at all. I post this script anyway even if I dircracing myself for not getting such a simple thing like one dimensinal offsets to work. I will have a look at it again after some sleep.
But that's why I post this becasue I need some help getting it right. So please can someone have a look and find out why I have commas in a few lines of the string / array output in the console. Must be some issue with the array index or the line break "\n" I've added everywhere. I wonder why I can print out the aTemplate array without joining it. Also I need to test if I have to choose inverted control settings depending on if its a left or right side bone.
I hope you don't mind that I borrowed the upper part of the script from the script example "Adjust Rigging to Shape"
That example just got the right checks and error messages for working with figure bones. I've included the Creative Commons Attribution 3.0 information in the header comment and included infromation that this is a modification of the original script example.
Update info:
GeneratePowerPoseTemplate_v09.dsa
I was bussy programming again and the code went over many iterations but now I can release an updated Script version that is working.
It is still in beta so to say because it only prints out the results into the console my final goal is to directly write the printed string arrays to DSX or TXT files.
I haven't got into writeFile ( String filename ) yet. Was reading some script examples but they extremly long. There is so much to declare and collecting paths and such, maybe I will add this later.
While the script iterates over the child bones you get the Script IDE console text blocks printed out, formatted in three different flavours: The default node listing now also includes node names.
I've fixed the issues with commas, empty first array indexes, extended the bone label search with the function setXYOffsetsByBoneLabel(sBoneLabel) and if((sBoneLabel.startsWith("Left")) bit this is still case sensitive.
I made experiments with trying to use Regular Expressions but that didn't went far. While I've cleaned up some issues new issues arised and still the offsets get completly messed up. As I tested the control point placements on the PoewerPose pane (by copy pasting console output to a template file) I had some kind of pyramide patterns and thats all this script can do for you I had to invert the left and right offsets because the face image is mirrored. In this release the offsets are completly messed up again with negative or high values exceeding the image boundaries. I made it too complicated and now I don't understand what it does with the offsets anymore. Anyway if you like to see the "debuging" of the "Bone Label Detection" in the console you need to hold down the Alt key while executing.
But just read the script comments its well documented with more details on the updates.
GeneratePowerPoseTemplate_v1.07a.dsa
Whith this version I was testing a way to detect and process children of child bones.
GeneratePowerPoseTemplate_v1.08a.dsa
Still testing but getting closer the main function now is only called once with generateTemplate("Head")the head bone got 7 children but I wanted it to work with all children of children. The script will now do the rest by its own to iterate over all child bones. Unfortunatly not in the right order as I thought I've added a loopCount print out to better see how the generateTemplate() function will process the child bones.
GeneratePowerPoseTemplate_v1.09b.dsa
OK now I figured out why generateTemplate("Head") was only finding the immediately children nodes because I was using getNodeChildren(false)all the time. This is the "Recurse" search switch parameter I was using for a flat hierarchy chain like with the Upper/Lower Face Rig bones but this was not what I wanted if I like to start with the "Head" bone. Now I have implemented both search methods to iterate over all children bones. By default the Recursive search method is used but you can also hold down the Shift key to use the other search method that I was using in v1.07a and v1.08a.
Download links:
- GeneratePowerPoseTemplate_v1.09b.dsa 34K 2019-07-14 Version: stable beta
-
GeneratePowerPoseTemplate_v1.08a.dsa 26K 2019-07-12 Version: testing alpha
-
GeneratePowerPoseTemplate_v1.07a.dsa 26K 2019-07-11 Version: testing alpha
-
GeneratePowerPoseTemplate_v09.dsa 20K 2019-06-23 Version: stable beta
- GeneratePowerPoseTemplateFile.dsa 7K 2019-06-20 Version: initial release
Screenshots of the console output:
Updated Script code:
Sorry with 685 lines of source code I can no longer add it to a code snipped. The forum page says Body is 3719 characters too long. I can only show the bottom part of the script here.
//This is only the bottom part of my script starting at line# 397 /********************************************************************* boolean : generateTemplate () constructs a string based on the bone label you give and the child bones on the selected figure *********************************************************************/ function generateTemplate( sStartWithBoneLabel ) { var aChildBones = []; //array of Child-Nodes of the given bone label - if any otherwise the single bone at index 0 print('\n\nSearch for a bone with the label: "' + sStartWithBoneLabel + '"'); var oBone = oFigure.findBoneByLabel( sStartWithBoneLabel ); if( !oBone ){ print('> Did not found a bone with the label: "' + sStartWithBoneLabel + '"' + sLine ); return false; //finished with error }else{ //======================================== //detect if the given bone got no children if ( bFindChildrenOfChildBonesRecursively ){ print('> found "' + sStartWithBoneLabel + '" the bone includes [' + oBone.getNodeChildren(true).length + '] recursively counted child nodes.' + sLine); if(oBone.getNodeChildren(true).length == 0){ //recursively count all this node's children aChildBones [0] = oBone; //also process single bones that don't have children by filling the first array index }else aChildBones = oBone.getNodeChildren(true); //recursively collect all this node's children } if ( !bFindChildrenOfChildBonesRecursively ){ print('> found "' + sStartWithBoneLabel + '" the bone includes [' + oBone.getNumNodeChildren() + '] immediate child nodes.' + sLine); //getNumNodeChildren() does not have a boolean parameter to count recursively if(oBone.getNumNodeChildren() == 0){ //count only this node's immediate children aChildBones [0] = oBone; //also process single bones that don't have children by filling the first array index } else aChildBones = oBone.getNodeChildren(false); //collect only this node's immediate children } /* Array : getNodeChildren( Boolean recurse=false ) Parameter(s): recurse - If true, recursively collect all this node's children, scanning the hierarchy from this node down. If false (default), collect only this node's immediate children. Note: can also include other nodes parented to the figure bone hierarchy */ //======================================== //main loop for ( var n = 0; n < aChildBones.length; n++ ) { sLabel = aChildBones[n].getLabel(); sName = aChildBones[n].getName(); //======================================== //if we don't have a bone in the array filled by getNodeChildren that can also include parented items that are not part of the figure skeleton if(!aChildBones[n].inherits( "DzBone" )){ aChildBones.slice(n, n+1); //remove this node from the array debugPrint('\n\n> Skipping the Node with the Label: "' + sLabel + '" ' + 'Name: "' + sName + '"'); debugPrint("\n\tthis is not a bone of the figure!" + sLine); continue; //skip this for loop, start next loop } //======================================== if( ( sLabel == "Upper Face Rig" ) || ( sLabel == "Lower Face Rig" ) && ( bFindChildrenOfChildBonesRecursively ) ){ aChildBones.slice(n, n+1); //remove this node from the array debugPrint('\n\n> Skipping the Node with the Name: "' + sName + '" ' + 'Label: "' + sLabel + '"'); debugPrint("\n\tWe do not need to create a control point!"); continue; //skip this for loop, start next loop } //======================================== //print the current node number, the LoopCount of this name and label to the console print("Bone: %1 | LoopCount: %2 | Name: %3 | Label: %4" .arg(aTemplate.length+1, 3) //fieldWidth=3 stands for a margin spaces of three digits eg. __5, __7, _15, 156 .arg(n+1, 3) //fieldWidth=3 print out for loop count .arg(sName, 20) //fieldWidth=20 for names - somehow right alignment? .arg(sLabel) ); debugPrint(sLine); //add a line if debugging for better readability //======================================== //set node attibutes if not already Selectable On, Hide in Scene View: Off see Joint Editor if(bManipChildBones){ if( !aChildBones[n].isSelectable () ){ //if the node is not selectable aChildBones[n].setSelectable(true); debugPrint("> Changed Node Attibute: Selectable: On"); } if( aChildBones[n].isHidden() ){ //if the node is set to Hide in Scene View aChildBones[n].setHidden(false); debugPrint("> Changed Node Attibute: Hide in Scene View: Off"); } } //======================================== //try to detemin the position of the control points by the bone label to get them rougthly positioned in the PowerPose pane if( !setXYOffsetsByBoneLabel(sLabel) ){ debugPrint("> the bone dosn't match any searched pattern." + sLine); resetOffsets(); debugPrint( getOffsets() ); } //======================================== // construct the PowerPose Template string array aTemplate[ aTemplate.length ] = //one dimensional array with one string for a bone : index == NodeNr + 1 "<node_data>\n" + "\t<x>" + nXoffset + "</x>\n" + "\t<y>" + nYoffset + "</y>\n" + "\t<node_label>" + sLabel + "</node_label>\n" + sControlSet01 + "</node_data>\n"; //======================================== //create another formated child bone list convertable to an Excel table if(bCreateChildBoneTable){ aChildBoneTable[ aChildBoneTable.length ] = (aChildBoneTable.length + 1) + sTableSeperator + sLabel; }///* /*======================================== Detect if the current child bone got children of its own - obsolete if we search recursively getNumNodeChildren(true). This was my workaround becasue oBone.getNodeChildren(fasle) was giving me only the immediate children nodes. Was working fine in a flat rigging bone hierarchy with Upper/Lower Face the I forgot what was written about in the API documentation. Parameter(s): recurse - If true, recursively collect all this node's children, scanning the hierarchy from this node down. If false (default), collect only this node's immediate children. ========================================*/ if ( (aChildBones[n].getNumNodeChildren() != 0) && !bFindChildrenOfChildBonesRecursively ){// var sChildrenLabelToStartWith = aChildBones[n].getNodeChild(0).getLabel(); //get the first child of the child bone at current index sChildrenLabelToStartWith = aChildBones[n].getLabel(); debugPrint('\n> Found a child bone with the Name: "' + sName + '"' + ' Label: "' + sLabel + '"' + '\n\tthat looks like it also got [' + aChildBones[n].getNumNodeChildren() + '] child bones!' + '\n\tNow the script will also search for: "' + sChildrenLabelToStartWith + '"' + sLine ); //fill the function call array with another instance of this function with giving the label of the child bone as argument aCallGenerateTemplate [aCallGenerateTemplate.length] = new generateTemplate( sChildrenLabelToStartWith ); }//*/ } //End of: for loop } //End of: if (!oBone){}else{} return true; //finished with success }; /********************************************************************* [Declaration#1] declaring working variables *********************************************************************/ var sScriptName = "GeneratePowerPoseTemplate"; var sLine = "\n--------------------------------------------------------------------------------\n"; var aErrorMsg = []; //instead of debugPrint() I could fill the error message array and show it at the end var aBones = oFigure.getAllBones (); //array of all figure bone objects var sLabel = oFigure.getLabel(); //first figure label - later bone label var sName = oFigure.getName(); //first figure name - later bone name //I try to construct a regular expression here to search anywhere for "left" and ignore case - I'm not shure if I understand the implementation of regular expressions in Daz Scripts yet// var reg_expLeft = /("left","i")/;// var reg_expLeft = /("[Ll]*eft*")/;// var reg_expRight = new RegExp( "^.*?\b(right)\b.*$","i" ); //another regular expression search, I think it says search "right" ignore case //offsets are related to the x/y pixel coordinates on the backround image //I just gave some values here so the points don't end up all on the same spot var nBkrndResX = 480; //horizontal resolution of the PowerPose backround images var nBkrndResY = 720; //vertical resolution of the PowerPose backround images var nXoffset = 0; //final x offset in relation to the center of the given backroud resolution var nXoffsetLeft = 0; //positive left offset in relation to the center var nXoffsetRight = 0; //negative right offset in relation to the center var nYoffset = 265; //the pixel height in which the points start to appear var bJumpedOnceVert = false; //apply the Yoffset jump once var nXspacing = 20; //how far the points get spaced horizontal var nYspacing = 20; //how far the points get spaced vertical var bFindChildrenOfChildBonesRecursively = true; //set this to false if this script should not detect and process children of child bones var bManipChildBones = true; //in order to edit the PowerPose template in the panes right-click menu "Edit Point" the properties of the processed bones gets changed var aCallGenerateTemplate = []; //this array will store additional generateTemplate() function calls with the bone label as argument that was detected as having child bones of its own var sChildrenLabelToStartWith = ""; //this is the bone label that was detected to have children of its own var bCreateChildBoneTable = false; //set this to false to skip creating a Child Bonde table for MS-Excel //this array can be written into a ascii text file that is convertable into a useful MS-Excel table var aChildBoneTable = []; //holds an arry of just bone number and bone label with no spaces, seperated by sTableSeperator eg. 1,Right Brow Inner,2,Right Brow Middle,3,Right Brow Outer,... var sTableSeperator = ","; //MS-Excel is importing text files with spaces or commas as seperators by default, semicolons work also var aTemplate = []; //the PoserPose template string array holds the output strings for all processed bones - can later get written to a DSX file //currently you need to manualy select & right-click copy the output from the ScriptIDE console and paste it to a template DSX file //======================================== var s_tpl_bg_file = "G3F-FC-Face.png"; //backround image var s_tpl_label_1 = "Body"; //first template selection point var s_tpl_label_2 = "Head"; //second template selection point var s_tpl_label_3 = ""; //n-i-u var s_tpl_label_4 = ""; var nl = "\n"; //insert new-line var sTemplateFileBOF = //Beginning Of File "<template_file>" +nl+ " <version>3</version>" +nl+ " <bg_file>" + s_tpl_bg_file + "</bg_file>" +nl+ " <width>" + nBkrndResX + "</width>" +nl+ " <height>" + nBkrndResY + "</height>" +nl+ " <template_points>" +nl+ " <tpl_data>" +nl+ " <x>84</x>" +nl+ " <y>56</y>" +nl+ " <label>" + s_tpl_label_1 + " Template</label>" +nl+ " <tpl_label>" + s_tpl_label_1 + "</tpl_label>" +nl+ " </tpl_data>" +nl+ " <tpl_data>" +nl+ " <x>362</x>" +nl+ " <y>56</y>" +nl+ " <label>" + s_tpl_label_2 + " Template</label>" +nl+ " <tpl_label>" + s_tpl_label_2 + "</tpl_label>" +nl+ " </tpl_data>" +nl+ " </template_points>"; var sTemplateFileEOF = //End Of File nl+ "</template_file>"; //you can choose which controls you want to have in the lower PowerPose pane - currently only sControlSet01 is used var sControlSet01 = "\t<lmb_horiz_prop>ytran</lmb_horiz_prop>\n" + "\t<lmb_vert_prop>xtran</lmb_vert_prop>\n" + "\t<rmb_horiz_prop>zrot</rmb_horiz_prop>\n" + "\t<rmb_horiz_sign>neg</rmb_horiz_sign>\n" + "\t<rmb_vert_prop>xrot</rmb_vert_prop>\n"; var sControlSet02 = "\t<lmb_horiz_prop>ytran</lmb_horiz_prop>\n" + "\t<lmb_vert_prop>xtran</lmb_vert_prop>\n" + "\t<rmb_horiz_prop>zrot</rmb_horiz_prop>\n" + "\t<rmb_horiz_sign>pos</rmb_horiz_sign>\n" //I guess if there is a 'neg' there must also be a 'pos' + "\t<rmb_vert_prop>xrot</rmb_vert_prop>\n"; /********************************************************************* [Start#] Main Script process *********************************************************************/// g_oScriptVersion.setVersionNumber ( 1,08,0,0 ); //void : setVersionNumber ( Number major, Number minor, Number revision, Number build )// print( sLine + g_sScriptFileName, "Version:", g_oScriptVersion.getVersionString (), "running." + sLine); // not working /*********************************************************************/ //print information about the running script if( g_sScriptPathFileName == "" ) { print( sLine + sScriptName, "running." + sLine); }else{ print( sLine + g_sScriptFileName, "running." + sLine); } //print information about the current modifier key state if( s_bAltPressed ){ //Alt key was pressed while executing the script print("> Alt Key was pressed while executing!\n" + "The script operates in debug mode now and will show additional inforamtions.\n\n"); }else print("> Alt Key was not pressed while executing!\n" + "The script operates in default mode now and will not show additional inforamtions.\n\n"); if( s_bShiftPressed ){ //Shift key was pressed while executing the script bFindChildrenOfChildBonesRecursively = false; //the script should not process all children of child bones at once but one after another print("> Shift Key was pressed while executing!\n" + "The script will now only find the immediate children of the searched bone\n" + "and will then jump to the next level down the figure bone hierarchy.\n\n"); }else print('> Shift Key was not pressed while executing!\n' + 'The script will "Recursively Find Children of Children"\n' + 'scanning the current figure rigging down to the last bone in the bone hierarchy chain.\n\n'); //print information about the current figure selection print( sLine + "The selected figure with label: ["+sLabel+"] name: ["+sName+"] got ["+aBones.length+"] bones." + sLine );/*// add two slashes at the beginning to execute that: //this was for testing and was printing out all bones at once for ( var n = 0 ; n < aBones.length ; n++ ) { sLabel = aBones[n].getLabel(); sName = aBones[n].getName(); print("Node: %1 | Name: %2 | Label: %3" .arg(n+1, 3) .arg(sName, 20) .arg(sLabel)); }//*/// remove two slashes at the beginning /********************************************************************* generateTemplate() function calls *********************************************************************////*// remove two slashes at the beginning to skip that: if( generateTemplate("Head")){ // try to start from the head bone resetOffsets(); }else{ return "Error! Something went wrong with finding: Head"; }//*/// remove two slashes at the beginning/*// add two slashes at the beginning to execute that: if( generateTemplate("Upper Face Rig") ){ nYoffset -= nYspacing; //the next row of control points will get shifted up }else return "Error! Something went wrong with finding: Upper Face Rig"; if( generateTemplate("Lower Face Rig") ){ }else return "Error! Something went wrong with finding: Lower Face Rig"; if( generateTemplate("Left Ear") ){ }else return "Error! Something went wrong with finding: Left Ear"; if( generateTemplate("Right Ear") ){ }else return "Error! Something went wrong with finding: Right Ear";*/// add two slashes at the beginning //======================================== // also process children of child bones if the function call array is not empty and we don't search recursively if ( !bFindChildrenOfChildBonesRecursively ){ for ( var m = 0; m < aCallGenerateTemplate.length; m++ ) { if( aCallGenerateTemplate[m] ) { }else{ return ("Error! Something went wrong with finding:", sChildrenLabelToStartWith ); } //something went wrong } } //======================================== // print the result(s) if(bCreateChildBoneTable){ //you can use this ascii text string to convert it into a MS-Excel table print("\n\nThe generated Excel table:" + sLine); print( aChildBoneTable.join( "\n" ) ); //New-Line seperator for getting a vertical row } print("\n\nThe generated PowerPose template:" + sLine); for( var i = 0; i < aTemplate.length; i++ ) { print( aTemplate[ i ] ); //well the print function can be quiet simple if you dont use .arg everywhere and the content is already a string :) } saveTemplateFile(); return "Finished. Everything went fine =)";// Finalize the function and invoke})( Scene.getPrimarySelection() );
Comments
The commas are from the default handling of Arrays by the print() command, which also seems to automatically do a .join("\n") for each element of the Array.
See "Mod_#1" in the attached v02.dsa I modified from your script. (Your original "obsolete loop?" was probably correct!).
See "Mod_#2" in the attached v02.dsa - I think it does what you want?: The <x> offsets for "Left" items start at 240 and decrease by 10 each time, and for "Right" items they start at 260 and increaseby 10 each time.
I'm sure you would have sorted it out quickly after a good sleep.
It would be great to know if you get the whole thing working in PowerPose...
Many thanks Praxis sorry for the late reply I had less time than I thought this weekend to rewrite a few things until I can now release an updated version.
Yeah I missused the array.join(); with empty parenthesis that is inserting a comma by default. But for now I don't have to join(); anything until I get to the part with writing to a file I guess. There was an expotentialy use of "\n" in every string and print but I see now that the print does a line break by itsself.
You're welcome - no problem about any delay.
No need for join() when writing to a file: Just use DzFile::writeLine( aTemplate[i] ) in a loop. There is an example of use (writing an .obj file) in the Add_Structure_v3.dsa file attached to this post
Good work!
I downloaded and ran your GeneratePowerPoseTemplate_v09.dsa script, copied the template generated xml code from the console into the G3F PowerPose genHead.dsx file, re-started DS, loaded a G3F, launched PowerPose - and there were 7 new "dots" for the Left, Mid, and Right brows - and they all work as you would want!
Seems you are not far from a finished product now...
Depends on how much this script should do. I've initialy amied at generating the Face template for Genesis 3 M/F first and if this is working see if I can expand it to create all other templates like Body, Hands and Head. I'm still working on the script and with my latest update where I try to start with searching for the the "Head" bone and try to detect if a child bone in the array got children further down the hierarchy I faced a design flaw that I'm currently try to solve to iterate over all childeren of the child bones. I thought of maybe better working exclusivly with arrays and array search methods and maybe creating multidimensional arrays for children of child bones.
I've posted the updated current version GeneratePowerPoseTemplate_v1.07a.dsa above.
But by your post I just realised that I never gave detailed instructions on how to use the genertated template of this script.
As mentioned before it all started in this thread How to Get PowerPose to Work with Genesis 3?
For testing purposes I've used this PowerPose template from PDSmith and manualy added a new Face Template: Powerpose - Legacy patches - G2 Total Overhaul. by PDSmith on DeviantArt.
The default location where to find power pose templates is DAZ 3D\DAZStudio4\plugins\PowerPose
you can also find the PDSmith updates for genesis 1, 2 & 3 in the Templates.dsx file..
Then I've created PowerPose "Addon" Template Sets for Genesis 3 and 8 Female by adding folders to my content directory and copying the modified PDSmiths DSX template files into this content path:
My Library\data\DAZ 3D\Genesis 3\Female\Tools\PowerPose\Sets\Genesis 3 Female with Face Control
If my script should ever write dsx files into the content directory I would choose a save path like this and a method to add those folders if they don't already exists. I saw some methods to do that in DzContentFolder. Actualy I should add this next - this could save some time while testing. You don't have to restart DS just change the template to body and back to face for updating and reading the modified DSX file.
...and all the other files of the PowerPose Set with their coresponding PNGs that will show as the backround in the pane:
After creating the folders, copy-pasting and editing the dsx files now I can select an alternative PowerPose Template Set in the dropdown menu on top of the pane. I did the same for Genesis 8 Female so I also get editable templates generated for her.
See the screenshots of my first tests posted in this thread: Why is there still no official Genesis 3 Powerpose template? (solution)
PowerPose_FaceTemplateG3F_02.png
PowerPose_FaceTemplateG8F_02.png
But as you can read in this thread in the post of RudyTL there is also a much easier "hacking" method of getting the encrypted *.dxe PowerPose template files of Genesis 8 to work with Genesis 3 figures. I've tested this "hack" and to my surprise it works quiet well with almost all Control Points in the Face and Head template.
Then I've used my script to compare the difference in bone labels of both figures and saw there are only 11 bones that differ - see the comparison in the attachment.
[Edit]: sorry the attached comparison tables as JPG an PDF files did't wanted to upload initialy becasue originaly there was a "&" character in the filename.
As saied before my script can only give you a starting point with placing the points, you then have to manualy edit them in the PowerPose pane by the righ-click menu Edit Mode then Edit Point for propper placement.
I hope my script can still be usfull to create PowerPose templates even for custom figures that are not supported yet. Therfore I tried to make it as universal as possible by ignoring upper/lower case but the bone label has to start with left/right. If the figure rig dosn't follow that naming convention you can use the search() method instead but in theory you can run in this script and the generateTemplate() function with any figure, any bone label and as often as you like to get your templates.
Once the Node Attributes: Hide in Scene View (see screenshot of Joint Editor Tool Settings) are set off for every face rig bone you can edit those points in the PowerPose pane Edit Mode that offers dialogs to manualy create, manipulate and save templates.
OK now I'm slowly making progress understanding how to use the "Child" and "Children" methods in my script. I was using getNodeChildren(false) all the time and wondered why I only get the immideate children. Then I found a workaround by adding a new function call into an array that should get processed at the end. Now I've implemeted both methods of iterating over the Children Nodes of the given bone label.
See my new updated script posted above: GeneratePowerPoseTemplate_v1.09b.dsa
By default the script will find all children of the given bone label by iterating down the figure node hierarchy until the last bone is found. If you press Shift while executing the script will use the previousely implemented method of iterating over all the children bones. I was ignoring the recurse search parameter in my getNodeChildren(false) method call. On the other hand I thought there is a recurse parameter for the getNumNodeChildren() method but this method dosn't have a parameter so I had to use a workarond with oBone.getNodeChildren(true).length.
This is the part where the script checks if the Shift key was pressed.
But I have a weired issue with the process flow in this execution mode. Since I create another function object of generateTemplate()then filling an array with those objects and at the end iterating over the arry of function calls. I thought those calls in the array will get processed at last. But as I can see by the LoopCount the function gets called immediately after creating a new function call.
Maybe this is not a good method but I wonder if I did something wrong or this is a bug in the script interperter? See the resulting Script IDE console output here GeneratePowerPoseTemplate_v1.09b_ProcessFlow-LoopCountConfusion.png or read the bottom script snipped. What do you think why this is happening?
Perhaps the program is more complicated than it needs to be?
I've attached a Minimalist.dsa script, derived from your v109b:
(updated 19-July to fix a bug. Attached .dsa updated as well)
This is a dump of the raw data for a G3F Figure - I think this provides the data you want?:
Hope this helps.
P.
Thanks, I apriciate this nice example. Something I can learn from how to structure the bone data. Its like a lesson in object oriented programming. Constructing new classes and defining members is something I can only vaguely rember from the programming course I took years ago. My script just bloaded up around the main for loop, the print out and the oBone.getNodeChildren() method because I didn't know how to do it otherwise. But you see this is a learning by doing project and at the end I hope to learn better daz scriting and programming. I already have alot of ideas I just have to learn how to express them in propper script code.
recursive function calls
I didn't know that you can recursively call a function within itself like this. I just guessed you need to use the new operator to somehow allocate memory for a new function call and the additionly local declared variables. And I think that focus on the current variables of the function is the point where my bone childrens gets processed in the wrong order.
variables decalaration scope
But I see within your example that a new oBoneData class gets constructed for every child bone in the array and thats exactly what I need. Also thanks for showing me how to fill a new element to an array with the .push() method. I see you are also using the new operator for declaring an arry. Is it better practice to write
new Array()
instead ofarray = []
? Also do I need to use the deleteLater() method on any object I've constructed with the new operator so it dosn't stay in memory when the script is done?new idea
By your example you gave me the idea of using the actual bone positions in 3D to place them on the PowerPose template. For front view point placement for the face I just have to multiply the x position with some value maybe counting it up in a loop and check the result until the resulting positive/negative horizontal control point positions reaches the backround border to dynamicaly scale the point positions to fit on the backround in any case. Then center the y position somehow maybe by substracting the starting bone y position from all the child bones. For the Hands I will need the x and z coordinated for a top down point placement. With this method you should get reasonable results for every part of any figure rig you run this over. I was already thinking of something like this while making the screenshot of the backround face once without and once with the figure rig bones visible in the viewport to position the points lateron.
exclusion list array argument
I already added a check to exlude the Upper/Lower Face rig bones for not creating control points for those but I will also have to give the GenerateTemplate() function an arry of exclude bones where to stop iterating over the child bones. This would make it possible for example to generate the body template by staring from the hip but exluding head, hands and face bones. Next generate the other templates and use another exclude list.
SaveTemplateFile() function
In the meanwhile I was writing on the SaveTemplateFile() function its looking like this right now:
I was able to extract the AssetUri from the figure node like this var sAssetUri = oAssetMgr.getAssetUriForNode( oFigure ); and construct the save path filename string for the template files. I thought normalizePath() would replace the %20 in the AssetUri with spaces but I had to manualy write something that replaces all %20 with spaces.
Otherwise I already have the path and filenames constructed and I have to check if the path already exists or if I have to create DzContentFolders and all that. Regarding the Error it seems like I don't understand the writeLine() method yet but I will have a look at the API Reference: http://docs.daz3d.com/doku.php/public/software/dazstudio/4/referenceguide/scripting/api_reference/object_index/file_dz
This is the console dump that I get if I have my Genesis 3 figure selected:
Updated SaveTemplateFile() function:
[Edit#1]: As soon as posted I saw a mistake I made you can't join strings and arrays with the plus operator like this aTemplate = sTemplateFileBOF + aTemplate + sTemplateFileEOF;
Now I've used the .unshift() and .push() methods with the aTemplates array to insert the strings at the begining and end but those had to be of type object so I've used var oTemplateFile = new Object(); and made the BOF and EOF strings members of those.
Old posted version:
SaveTemplateFile() function
TypeError: Result of expression 'oTemplateFile.writeLine' [undefined] is not a function.
In function WritingFile( sTemplatePathFileName, aTemplate )
I think you need to change this:
var oTemplateFile = new DzFileInfo( sTemplatePathFileName );
to this?:
var oTemplateFile = new DzFile( sTemplatePathFileName );
new idea
For that purpose you will probably want to use oBone.getEndPoint() instead of oBone.getWSPos().
The Bone getWSPos() positions appear to be the same as the getOrigin() positions, which for the Eyelid bones often have the same x and y values. getEndPoint() usually provides distinct x and y positions for those bones.
recursive function calls
The extractBonesData() function in my post is an example of the standard "Depth-First" algorithm for recursively traversing a Tree structure (e.g. the same mechanism can be used for traversing a file directory structure).
Recursive functions are compact, but harder to debug. And if they go too deep (too many Levels) then you get the dreaded Stack overflow problem. I don't know what DAZ Script's Stack limits are, but I doubt we are anywhere near them for this purpose. e.g. If you execute extractBonesData( "hip", 0 ) on G3F the recursion goes to 15 Levels (e.g. bone "rIndex3") without any problem, and lists 172 bones.
variables decalaration scope
re oBoneData in my post: I'm learning too - I didn't know about that way of defining/creating new classes "informally, on the fly" until I found this page after reading this post by Rob just last week. Of course, that has always been documented by DAZ in the example code just above "constructor" for Object , but I didn't understand the implications until now.
It looks like a very useful mechanism - e.g. you could use it to easily return multiple values from any function, instead of just a simple Boolean or Number, etc.:
I suspect it does not matter, but I don't know. Perhaps someone can clarify this?
Again, I don't know - I use it after new DzFileInfo() only because I've seen that done in the DAZ Sample scripts. The Qt documentation for deleteLater() is not very helpful in the context of DAZ Script.
DAZ Script uses automatic garbage collection, but it may be that special treatment such as deleteLater() is necessary when OS handles, etc. are involved: As that Wikipedia article says: "Resources other than memory, such as network sockets, database handles, user interaction windows, file and device descriptors, are not typically handled by garbage collection."
For example: In the DAZ Sample script SaveFilter_Template_Batch.dsa, deleteLater() is used for all of these:
I'm glad I could help. Keep going!
P.
What would this need IOT make PowerPose templates for Legacy-Rigged figures like Mike3, or V2, or Apollo Maximus?
The other question is why would you need another PowerPose template for the Legacy-Rigged figures? If everything works right on your side a "generic" PowerPose template should show up with those older figures.
For testing I've just loaded up Aiko 3 and looked at my PowerPose pane and the Default Female template shows up that offers all default control points for this kind of figure rig. You get three templates for the head, body and hands. Compared to the Genesis 3 and 8 templates those are rather smale because there is a fixed resolution for the backround image.
If you plan to create your own templates by replacing the generic template for those figures keep in mind that the older figure templates until genesis 3 are threaded differently and are saved in the default plugins folder eg. C:\DAZ 3D\DAZStudio4\plugins\PowerPose\Templates.dsx. This is the file you would have to modifiy if you want to add custom PowerPose templates for figures prior to genesis 3.
How to rewrite the script to make it work for Legacy-Rigged figures
Basicly you have to remove the check for
// If we don't have a weight mapped figure
starting at line 146 in my last posted script version 1.09b. Actualy this check is not needed in my script but leftover because I was copy-pasting it from the "adjust rigging to shape" script example.Then you have to run it more than once with different bones to start with for example generateTemplate("Head") these lines are located towards the end of the script. This will give you all the <node_data> output for the head bones that goes into the head.dsx template file. Next you would run generateTemplate("Left Hand")and generateTemplate("Right Hand").
Creating the Body.dsx template and picking the right nodes for all limbs but not the hands, the head but not the eyes, is a bit of an issue with my script because you would have to start with generateTemplate("Hip")but this will include all bones of the figure not just the limbs. I haven't implemented an exclude list yet but you could simply remove the unwanted <node_data>.
Like mentioned before you have to copy the Script IDE console output with the <node_data> list to the right DSX file in your library. This is the Genesis Face Template example to show how a template file should look like.
Once you have the DSX file templates for Head, Body, Hands you need to modify the C:\DAZ 3D\DAZStudio4\plugins\PowerPose\Templates.dsx where all figure tempates are referenced to tell PowerPose which templates to load with which figure. I have installed the Legacy Patches Powerpose - Legacy patches - G2 Total Overhaul. by PDSmith on DeviantArt and this was overwriting my original Templates.dsx file. Also I completly removed the Genesis 3 references (I have copied the genesis 8 templates to use them with G3 in the "data" folder of my content library)
I guess you would need to add "Mike3" or "Apollo Maximus" into the list of generic Male to get default generic controls for this figure.
Or something like that you have to look for the internal figure name not the label you see in the scene pane. You see the Templates.dsx file also includes a template for dragon, snake tail mermaid tail and what not. For a custom template for a specific Legacy-Rigged figure you would have to add another section starting like this eg. <tplset> <name>Mike3</name> ...</tplset>.
I hope this can help and sorry for that my script isn't as user frindly and complete as I wanted it yet. I made some progress further than v1.09 but currently I got stuck with v1.14 and no matter what I tried I didn't get it to save a DSX file to disk but thats a story for another post.
Here is how my Templates.dsx looks right now. If you get issues with older figures not showing a propper powerpose template I would first have a look into that file. It can get overwritten by an update and your modifications are gone. That is the weakness of the old Templates.dsx handling all figures at once and saved in the plugins folder.
SD, what an investment of time! Kudos to you. I came across your thread because I was searching for more about templates for Power Pose. The generic template is missing alot when I tried to use it with Plushies 2.0. I am not a coder and tried to follow along in the thread. Would your script work to generate a more accurate template? (I don't need the graphic to match the plush shape, per se, just accurate dots to manipulate the bones.)