PowerPose Templates generator script

Syrus_DanteSyrus_Dante Posts: 983

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"

Source: http://docs.daz3d.com/doku.php/public/software/dazstudio/4/referenceguide/scripting/api_reference/samples/nodes/adjust_rigging_to_shape/start

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:

Screenshots of the console output:

GeneratePowerPoseTemplate_v1.09b_DefaultExecution.png GeneratePowerPoseTemplate_v1.09b_ShiftKeyExecution.png GeneratePowerPoseTemplate_v1.09b_ProcessFlow-LoopCountConfusion 

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() );
dsa
dsa
GeneratePowerPoseTemplate_v09.dsa
20K
dsa
dsa
GeneratePowerPoseTemplateFile.dsa
7K
GeneratePowerPoseTemplate_v09b.dsa_001.png
660 x 1036 - 66K
GeneratePowerPoseTemplate_v09b.dsa_002.png
660 x 1036 - 76K
GeneratePowerPoseTemplate_v09b.dsa_003.png
660 x 1036 - 56K
dsa
dsa
GeneratePowerPoseTemplate_v1.07a.dsa
26K
test_GetScript-Paths-FileName_v1.07a_001.png
674 x 995 - 74K
test_GetScript-Paths-FileName_v1.07a_002.png
674 x 995 - 79K
dsa
dsa
GeneratePowerPoseTemplate_v1.08a.dsa
26K
GeneratePowerPoseTemplate_v1.09b_DefaultExecution.png
718 x 1036 - 64K
GeneratePowerPoseTemplate_v1.09b_ShiftKeyExecution.png
718 x 1036 - 65K
GeneratePowerPoseTemplate_v1.09b_ProcessFlow-LoopCountConfusion.png
718 x 1036 - 63K
dsa
dsa
GeneratePowerPoseTemplate_v1.09b.dsa
34K
Post edited by Syrus_Dante on

Comments

  • PraxisPraxis Posts: 123
    ... find out why I have commas in a few lines of the string / array output in the console.

     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!).

     

    ... depending on if its a left or right side bone.

    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...

     

    dsa
    dsa
    v02.dsa
    9K
  • Syrus_DanteSyrus_Dante Posts: 983

    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.

  • PraxisPraxis Posts: 123

    You're welcome - no problem about any delay.

     

    ... for now I don't have to join(); anything until I get to the part with writing to a file I guess.

    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

     

     

     

  • PraxisPraxis Posts: 123

    Good work!yes

    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...

     

  • Syrus_DanteSyrus_Dante Posts: 983
    edited July 12

    Praxis saied:

    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.

    • Templates.dsx - this is the main Genesis 3 Female Template Set file I've modified with references to the other DSX templates that have the node controls.
    <template_suite>
     <version>2</version>
     <template_sets>
      <template_set>
       <name>Genesis 3 Female</name>
       <dynamics_file>bipedDynamics.dsx</dynamics_file>
       <geometries>
        <name>Genesis3Female</name>
       </geometries>
       <templates>
        <template>
         <label>Body</label>
         <file>G3F-FC-Body.dsx</file>
        </template>
        <template>
         <label>Hands</label>
         <file>G3F-FC-Hands.dsx</file>
        </template>
        <template>
         <label>Head</label>
         <file>G3F-FC-Head.dsx</file>
        </template>
        <template>
         <label>Face</label>
         <file>G3F-FC-Face.dsx</file>
        </template>
       </templates>
      </template_set>
     </template_sets>
    </template_suite>
    • G3F-FC-Face.dsx - This is the face template overhead with the <template_points> I'm referencing the Head and Body templates. Those are the arrow icons placed at the top for easy navigation, you can paste the PowerPose template with all the <node_data> generated by this script between the two #comment lines
    <template_file>
     <version>3</version>
     <bg_file>G3F-FC-Face.png</bg_file>
     <width>450</width>
     <height>675</height>
     <template_points>
      <tpl_data>
       <x>84</x>
       <y>56</y>
       <label>Head Template</label>
       <tpl_label>Head</tpl_label>
      </tpl_data>
      <tpl_data>
       <x>362</x>
       <y>56</y>
       <label>Body Template</label>
       <tpl_label>Body</tpl_label>
      </tpl_data>
     </template_points>
    #insert generated PowerPose template here:
    <node_data>
    	<x>220</x>
    	<y>292</y>
    	<node_label>Right Brow Inner</node_label>
    	<lmb_horiz_prop>ytran</lmb_horiz_prop>
    	<lmb_vert_prop>xtran</lmb_vert_prop>
    	<rmb_horiz_prop>zrot</rmb_horiz_prop>
    	<rmb_horiz_sign>neg</rmb_horiz_sign>
    	<rmb_vert_prop>xrot</rmb_vert_prop>
    </node_data>
    <node_data>
    ...
    </node_data>
    #finish the template file with:
    </template_file>

    ...and all the other files of the PowerPose Set with their coresponding PNGs that will show as the backround in the pane:

    • bipedDynamics.dsx
    • G3F-FC-Body.dsx
    • G3F-FC-Body.png
    • G3F-FC-Hands.dsx
    • G3F-FC-Hands.png
    • G3F-FC-Head.dsx
    • G3F-FC-Head.png

    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.

      

    Daz3D Genesis 3-8 Upper-LowerFaceRig Comparison_01.png
    727 x 1344 - 196K
    pdf
    pdf
    Daz3D Genesis 3-8 Upper-LowerFaceRig Comparison_01.pdf
    181K
    Post edited by Syrus_Dante on
  • Syrus_DanteSyrus_Dante Posts: 983

    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.

    [Quote]: docs.daz3d.com/doku.php/public/software/dazstudio/4/referenceguide/scripting/api_reference/object_index/node_dz

    DzNode :: findNodeChild ( String name, Boolean recurse=false )

    DzNode :: findNodeChildByLabel ( String label, Boolean recurse=false )

    Array :: getNodeChildren ( Boolean recurse=false )

    Number :: getNumNodeChildren ()

     This is the part where the script checks if the Shift key was pressed.

    //script line: 616
    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

    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?

    //script line: 501
    if ( (aChildBones[n].getNumNodeChildren() != 0)
    	&& !bFindChildrenOfChildBonesRecursively ){
    	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 );
    }
    
    //script line: 663
    //========================================
    // 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
    	}
    }
    //Script IDE Console print out:
    --------------------------------------------------------------------------------
    GeneratePowerPoseTemplate_v1.09b.dsa running.
    --------------------------------------------------------------------------------
    > Alt Key was not pressed while executing!
    The script operates in default mode now and will not show additional inforamtions.
    
    > Shift Key was pressed while executing!
    The script will now only find the immediate children of the searched bone
    and will then jump to the next level down the figure bone hierarchy.
    
    --------------------------------------------------------------------------------
    The selected figure with label: [Genesis 3 Female] name: [Genesis3Female] got [172] bones.
    --------------------------------------------------------------------------------
    
    Search for a bone with the label: "Head"
    > found "Head" the bone includes [7] immediate child nodes.
    --------------------------------------------------------------------------------
    Bone:   1 | LoopCount:   1 | Name:           upperTeeth | Label: Upper Teeth
    Bone:   2 | LoopCount:   2 | Name:             lowerJaw | Label: Lower Jaw
    
    Search for a bone with the label: "Lower Jaw"
    > found "Lower Jaw" the bone includes [2] immediate child nodes.
    --------------------------------------------------------------------------------
    Bone:   3 | LoopCount:   1 | Name:           lowerTeeth | Label: Lower Teeth
    
    .
    ..
    ...
    
    Search for a bone with the label: "Lower Face Rig"
    > found "Lower Face Rig" the bone includes [18] immediate child nodes.
    --------------------------------------------------------------------------------
    Bone:   9 | LoopCount:   1 | Name:     lNasolabialLower | Label: Left Nasolabial Lower
    .
    .
    .
    Bone:  26 | LoopCount:  18 | Name:           rJawClench | Label: Right Jaw Clench
    
    //Now at the end the remaining "Head" bone [7] immediate child nodes gets processed
    
    Bone:  27 | LoopCount:   4 | Name:                 lEye | Label: Left Eye
    Bone:  28 | LoopCount:   5 | Name:                 rEye | Label: Right Eye
    Bone:  29 | LoopCount:   6 | Name:                 lEar | Label: Left Ear
    Bone:  30 | LoopCount:   7 | Name:                 rEar | Label: Right Ear
  • PraxisPraxis Posts: 123
    edited July 18

    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)

    // Minimalist script to extract Face-Rig data from a G3F or G8F Figure.
    
    // Define an anonymous function;
    // serves as our main routine,
    // limits the scope of variables
    (function(){
    
      // Array of data about each Bone:
      var aBonesData = new Array();
    
      //  Each entry in the Array is an Object with this structure:
      //    { nLevel
      //    , sName
      //    , sLabel
      //    , x
      //    , y
      //    , z
      //    }
    
    
      //---------------------------------------------------------
      //---function to extract the data for one Bone hierarchy---
      //---------------------------------------------------------
      // NB: This is a RECURSIVE function
    
      function extractBonesData( oBone, nLevel ) {
        if( oBone && (oBone.inherits( "DzBone" )) ){
    
          //---Process this Bone---
          var vPos = oBone.getWSPos();
    
          var oBoneData = new Object();
          oBoneData.nLevel  = nLevel;
          oBoneData.sName   = oBone.getName();
          oBoneData.sLabel  = oBone.getLabel();
          oBoneData.x       = vPos.x;
          oBoneData.y       = vPos.y;
          oBoneData.z       = vPos.z;
    
          aBonesData.push( oBoneData );
    
    
          //---Process this Bone's Child Bones---
          var aChildNodes = oBone.getNodeChildren( false );     // false: Don't recurse: Just return the immediate Child Nodes
          for( var n = 0; n < aChildNodes.length; n++ ) {
            extractBonesData( aChildNodes[n], nLevel + 1 );     // <---RECURSIVE call
          }
    
        }
      } // extractBonesData()
    
    
      //----------------------------------------------
      //---Main Routine---
      //----------------------------------------------
    
      var oFigure = undefined;
    
      var oNode = Scene.getPrimarySelection();
      if( oNode ) {
        if( oNode.inherits( "DzBone" ) ){
          oNode = oNode.getSkeleton();
        }
        if( oNode.inherits( "DzFigure" ) ){
          oFigure = oNode;
        }
      }
    
      if( !oFigure ) {
        print( "No suitable Figure is Selected" );
      } else {
        print( oFigure.getName() +": "+ oFigure.getLabel() );
    
        //---Extract the data for each desired Bone hierarchy---
        // Use Node Names instead of Labels, to reduce DS Version dependency:
        //  Get Names by inspection in the Scene Pane's Node sub-panel.
        //  true: Search recursively if necessary
        //  Initial Level = 0
        extractBonesData( oFigure.findNodeChild( "upperFaceRig", true ), 0 );
        extractBonesData( oFigure.findNodeChild( "lowerFaceRig", true ), 0 );
        extractBonesData( oFigure.findNodeChild( "lEar"        , true ), 0 );
        extractBonesData( oFigure.findNodeChild( "rEar"        , true ), 0 );
        // Results are accumulated in Array aBonesData
    
    
        //---Output the data---
        print( " " );
        var oBoneData;
        for( var n = 0; n < aBonesData.length; n++ ) {
          oBoneData = aBonesData[n];
    
          // Q&D: Just dump the raw data to the console:
          print( oBoneData.nLevel
               , String("%1").arg( oBoneData.sName, 25)
               , String("%1").arg( oBoneData.x.toFixed(1), 5)
               , String("%1").arg( oBoneData.y.toFixed(1), 5)
               , String("%1").arg( oBoneData.z.toFixed(1), 5)
               , oBoneData.sLabel
               );
        }
        print( " " );
    
      }
    
    // Finalize the function and invoke
    })();
    
    

    This is a dump of the raw data for a G3F Figure - I think this provides the data you want?:

    Genesis3Female: Genesis 3 Female
    
    ----- --------------------- ----- ----- ----- -----------------------
    Level                  Name   X     Y     Z   Label
    ----- --------------------- ----- ----- ----- -----------------------
    0              upperFaceRig   0.0 162.6  -1.8 Upper Face Rig
    1                rBrowInner  -2.0 170.4   8.8 Right Brow Inner
    1                  rBrowMid  -3.9 170.7   8.5 Right Brow Middle
    1                rBrowOuter  -5.5 170.1   7.3 Right Brow Outer
    1                lBrowInner   2.0 170.4   8.8 Left Brow Inner
    1                  lBrowMid   3.9 170.7   8.5 Left Brow Middle
    1                lBrowOuter   5.5 170.1   7.3 Left Brow Outer
    1                CenterBrow   0.0 170.3   9.2 Center Brow
    1             MidNoseBridge   0.0 168.3   9.0 Middle Nose Bridge
    1              lEyelidInner   3.3 168.6   6.2 Left Eyelid Inner
    1         lEyelidUpperInner   3.3 168.6   6.2 Left Eyelid Upper Inner
    1              lEyelidUpper   3.3 168.6   6.2 Left Eyelid Upper
    1         lEyelidUpperOuter   3.3 168.6   6.2 Left Eyelid Upper Outer
    1              lEyelidOuter   3.3 168.6   6.2 Left Eyelid Outer
    1         lEyelidLowerOuter   3.3 168.6   6.2 Left Eyelid Lower Outer
    1              lEyelidLower   3.3 168.6   6.2 Left Eyelid Lower
    1         lEyelidLowerInner   3.3 168.6   6.2 Left Eyelid Lower Inner
    1              rEyelidInner  -3.3 168.6   6.2 Right Eyelid Inner
    1         rEyelidUpperInner  -3.3 168.6   6.2 Right Eyelid Upper Inner
    1              rEyelidUpper  -3.3 168.6   6.2 Right Eyelid Upper
    1         rEyelidUpperOuter  -3.3 168.6   6.2 Right Eyelid Upper Outer
    1              rEyelidOuter  -3.3 168.6   6.2 Right Eyelid Outer
    1         rEyelidLowerOuter  -3.3 168.6   6.2 Right Eyelid Lower Outer
    1              rEyelidLower  -3.3 168.6   6.2 Right Eyelid Lower
    1         rEyelidLowerInner  -3.3 168.6   6.2 Right Eyelid Lower Inner
    1              lSquintInner   2.7 167.3   7.7 Left Squint Inner
    1              lSquintOuter   5.1 167.6   7.0 Left Squint Outer
    1              rSquintInner  -2.7 167.3   7.7 Right Squint Inner
    1              rSquintOuter  -5.1 167.6   7.0 Right Squint Outer
    1               lCheekUpper   5.5 165.7   6.8 Left Cheek Upper
    1               rCheekUpper  -5.5 165.7   6.8 Right Cheek Upper
    1                      Nose   0.0 164.9  10.4 Nose
    1                  lNostril   0.7 164.6   9.6 Left Nostril
    1                  rNostril  -0.7 164.6   9.6 Right Nostril
    1             lLipBelowNose   1.3 163.3   8.6 Left Lip Below Nose
    1             rLipBelowNose  -1.3 163.3   8.6 Right Lip Below Nose
    1            lLipUpperOuter   2.1 162.1   8.5 Left Lip Upper Outer
    1            lLipUpperInner   1.1 162.2   8.8 Left Lip Upper Inner
    1            LipUpperMiddle   0.0 162.5   9.2 Lip Upper Middle
    1            rLipUpperInner  -1.1 162.2   8.8 Right Lip Upper Inner
    1            rLipUpperOuter  -2.1 162.1   8.5 Right Lip Upper Outer
    1      lLipNasolabialCrease   2.5 162.8   8.6 Left Lip Nasolabial Crease
    1      rLipNasolabialCrease  -2.5 162.8   8.6 Right Lip Nasolabial Crease
    1          lNasolabialUpper   1.7 166.3   8.6 Left Nasolabial Upper
    1          rNasolabialUpper  -1.7 166.3   8.6 Right Nasolabial Upper
    1         lNasolabialMiddle   2.9 164.5   8.2 Left Nasolabial Middle
    1         rNasolabialMiddle  -2.9 164.5   8.2 Right Nasolabial Middle
    0              lowerFaceRig   0.0 164.1   0.1 Lower Face Rig
    1          lNasolabialLower   3.0 159.6   6.6 Left Nasolabial Lower
    1          rNasolabialLower  -3.0 159.6   6.6 Right Nasolabial Lower
    1    lNasolabialMouthCorner   4.2 161.9   6.8 Left Nasolabial Mouth Corner
    1    rNasolabialMouthCorner  -4.2 161.9   6.8 Right Nasolabial Mouth Corner
    1                lLipCorner   3.3 161.7   7.3 Left Lip Corner
    1            lLipLowerOuter   2.1 161.1   8.1 Left Lip Lower Outer
    1            lLipLowerInner   1.1 160.9   8.4 Left Lip Lower Inner
    1            LipLowerMiddle   0.0 160.7   8.7 Lip Lower Middle
    1            rLipLowerInner  -1.1 160.9   8.4 Right Lip Lower Inner
    1            rLipLowerOuter  -2.1 161.1   8.1 Right Lip Lower Outer
    1                rLipCorner  -3.3 161.7   7.3 Right Lip Corner
    1                  LipBelow   0.0 159.6   8.4 Lip Below
    1                      Chin   0.0 158.3   7.4 Chin
    1               lCheekLower   5.5 162.1   5.2 Left Cheek Lower
    1               rCheekLower  -5.5 162.1   5.2 Right Cheek Lower
    1                  BelowJaw   0.0 157.1   4.7 Below Jaw
    1                lJawClench   6.1 161.8   0.8 Left Jaw Clench
    1                rJawClench  -6.1 161.8   0.8 Right Jaw Clench
    0                      lEar   6.9 166.6  -1.3 Left Ear
    0                      rEar  -6.9 166.6  -1.3 Right Ear
    ----- --------------------- ----- ----- ----- -----------------------
    

    Hope this helps.

    P.

    dsa
    dsa
    Minimalist.dsa
    3K
    Post edited by Praxis on
  • Syrus_DanteSyrus_Dante Posts: 983
    edited July 20

    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 of array = []? 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:

    sAssetUri: /data/DAZ%203D/Genesis%203/Female/Genesis3Female.dsf#Genesis3Female
    sFigureName: Genesis3Female
    sAssetCompBase: /Genesis 3/Female
    sNodeDataPath: data/DAZ 3D/Genesis 3/Female
    sSavePath: Z:/DAZStudio - Content Directories/Library Main/data/DAZ 3D/Genesis 3/Female/Tools/PowerPose/Sets/Generated PowerPose Template
    sTemplatePathFileName: Z:/DAZStudio - Content Directories/Library Main/data/DAZ 3D/Genesis 3/Female/Tools/PowerPose/Sets/Generated PowerPose Template/Genesis3Female-Head.dsx
    Script Error: Line 253
    TypeError: Result of expression 'oTemplateFile.writeLine' [undefined] is not a function.

    Updated SaveTemplateFile() function:

    	//----------------------------------------------------------------------------
    	// Utility Routine: Return true if the specified file exists
    	//----------------------------------------------------------------------------
    	function util_fileExists( sFileSpec )    // : Boolean
    	{
    		var bResult = false;
    		var oFileInfo = new DzFileInfo( sFileSpec );
    		if( oFileInfo.exists() ) {
    			bResult = true;
    		}
    		// v4 Sample scripts always do this:
    		oFileInfo.deleteLater();        // Don't leak memory
    		return bResult;
    	};  // util_fileExists()
    
    	/*********************************************************************
    	boolean : WritingFile()
    	*********************************************************************/
    	function WritingFile( sTemplatePathFileName, aTemplate )
    	{
    		var oTemplateFile = new DzFileInfo( sTemplatePathFileName );
    		for (var i = 0; i < aTemplate.length; i++){
    			oTemplateFile.writeLine( aTemplate[i] );
    		}
    	};
    
    	/*********************************************************************
    	bool : SaveTemplateFile()
    	*********************************************************************/
    	function SaveTemplateFile( sTemplateName, aTemplate )
    	{
    		var sTemplateSetPath = "Tools/PowerPose/Sets";		//default location for PowerPose Add-On Sets
    		var sTemplateSetName = "Generated PowerPose Template";		//the name of the PowerPose Set
    
    		var oAssetMgr = App.getAssetMgr();
    		if( oAssetMgr ){
    			//String : getAssetUriForNode( DzNode node )
    			var sAssetUri = oAssetMgr.getAssetUriForNode( oFigure );
    			var sFigureName = sAssetUri.substring( 1 + sAssetUri.searchRev("#") );
    			var sNodeDataPath = sAssetUri.left( sAssetUri.searchRev( '/' ) );		//only keep what is left from the last / in the string
    			//String : getCompatibilityBasePathForNode( DzNode node ) 
    			var sAssetCompBase = oAssetMgr.getCompatibilityBasePathForNode( oFigure );		//A string version of the node's compatibility base, or an empty string if none exists.
    			/* String : normalizePath( String type, Boolean relative=true )
    			Parameter(s): type - The path/type to be normalized    relative - Whether or not this is a relative (or absolute) path
    			Return Value: A string with a normalized path ???
    			--------------------------------------------------------------------------------
    			I thought this would replace all the %20 with "normal" spaces that where returned from getAssetUriForNode()
    			but this does not work :( */
    			sNodeDataPath = oAssetMgr.normalizePath( sNodeDataPath, true );
    			var bNothingToReplace = false;
    			while( !bNothingToReplace ) {		//replace all %20 with spaces from the "AssetUri" string
    				sNodeDataPath = sNodeDataPath.replace("%20", " ");
    				if( sNodeDataPath.search("%20") == -1 ){
    					bNothingToReplace = true;
    				}
    			}
    			print("sAssetUri:", sAssetUri);
    			print("sFigureName:", sFigureName);
    			print("sAssetCompBase:", sAssetCompBase);
    			print("sNodeDataPath:", sNodeDataPath);
    		}else return "Error! Could not get App.getAssetMgr()";
    		var oContentMgr = App.getContentMgr();
    		if( oContentMgr ){
    			var sBaseContentDir = oContentMgr.getContentDirectoryPath( 0 );	// Set the base directory to the first mapped (index 0) native formats directory - that is also referred to as main content library
    			var sSavePath = sBaseContentDir + "/" + sNodeDataPath + "/" + sTemplateSetPath + "/" + sTemplateSetName;		//construct the absolut save path
    			var sTemplatePathFileName = sSavePath + "/" + sFigureName + "-" + sTemplateName + ".dsx";			//the final path and filename of the template file to save
    			print("sSavePath:", sSavePath );
    			print("sTemplatePathFileName:", sTemplatePathFileName );
    		}else return "Error! Could not get App.getContentMgr()";
    
    		var s_tpl_bg_file = sFigureName + "-" + sTemplateName + ".png";	//backround image reference
    		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 = "Face";
    		var s_tpl_label_4 = "Hands";
    		var nl = "\n";	//insert new-line
    		var oTemplateFile = new Object();
    			oTemplateFile.BOF	= "<template_file>"		//Beginning Of 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+	"  <tpl_data>"
    			+nl+	"   <x>84</x>"
    			+nl+	"   <y>76</y>"
    			+nl+	"   <label>" + s_tpl_label_3 + " Template</label>"
    			+nl+	"   <tpl_label>" + s_tpl_label_3 + "</tpl_label>"
    			+nl+	"  </tpl_data>"
    			+nl+	"  <tpl_data>"
    			+nl+	"   <x>362</x>"
    			+nl+	"   <y>76</y>"
    			+nl+	"   <label>" + s_tpl_label_4 + " Template</label>"
    			+nl+	"   <tpl_label>" + s_tpl_label_4 + "</tpl_label>"
    			+nl+	"  </tpl_data>"
    			+nl+	" </template_points>"
    			+nl;
    		oTemplateFile.EOF =	//End Of File
    			"</template_file>";
    
    		aTemplate.unshift( oTemplateFile.BOF );	//insert the beginning of the file at the beginning of the aTemplate array
    		aTemplate.push( oTemplateFile.EOF );		//insert the end of the file at the end of the aTemplate array
    
    		print( aTemplate.join("") );
    
    		if(util_fileExists( sTemplatePathFileName )){
    			return false;
    		}else WritingFile( sTemplatePathFileName, aTemplate );
    		return true;
    	};

    [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:

    		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+	"  <tpl_data>"
    			+nl+	"   <x>84</x>"
    			+nl+	"   <y>76</y>"
    			+nl+	"   <label>" + s_tpl_label_3 + " Template</label>"
    			+nl+	"   <tpl_label>" + s_tpl_label_3 + "</tpl_label>"
    			+nl+	"  </tpl_data>"
    			+nl+	"  <tpl_data>"
    			+nl+	"   <x>362</x>"
    			+nl+	"   <y>76</y>"
    			+nl+	"   <label>" + s_tpl_label_4 + " Template</label>"
    			+nl+	"   <tpl_label>" + s_tpl_label_4 + "</tpl_label>"
    			+nl+	"  </tpl_data>"
    			+nl+	" </template_points>";
    		var sTemplateFileEOF =	//End Of File
    			nl+	"</template_file>";
    
    		aTemplate = sTemplateFileBOF + aTemplate + sTemplateFileEOF;
    
    		if(util_fileExists()){
    			return false;
    		}else WritingFile( sTemplatePathFileName, aTemplate );
    		return true;
    	};
    Post edited by Syrus_Dante on
  • PraxisPraxis Posts: 123
    edited July 20

    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.:

    var oResult = new Object();
    oResult.bValid = true;
    oResult.nValue = 42;
    oResult.vEndpoint = oBone.getEndpoint();
    oResult.aNodeChildren = oBone.getNodeChildren( false );
    // ...etc.
    return oResult;
    
    

     

    ... I see you are also using the new operator for declaring an arry. Is it better practice to write new Array()instead of array = []?

    I suspect it does not matter, but I don't know.  Perhaps someone can clarify this?

     

    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?

    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:

    • DzFileInfo
    • DzScript
    • DzAssetIOFilter
    • DzDir

     

    I'm glad I could help.  Keep going!

    P.

     

    Post edited by Praxis on
  • Eustace ScrubbEustace Scrubb Posts: 2,432

    What would this need IOT make PowerPose templates for Legacy-Rigged figures like Mike3, or V2, or Apollo Maximus?

  • Syrus_DanteSyrus_Dante Posts: 983
    edited August 23

    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&#39;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.

    <template_file>
     <version>3</version>
     <bg_file>G3F-FC-Face.png</bg_file>
     <width>450</width>
     <height>675</height>
     <template_points>
      <tpl_data>
       <x>84</x>
       <y>56</y>
       <label>Head Template</label>
       <tpl_label>Head</tpl_label>
      </tpl_data>
      <tpl_data>
       <x>362</x>
       <y>56</y>
       <label>Body Template</label>
       <tpl_label>Body</tpl_label>
      </tpl_data>
     </template_points>
    #insert generated PowerPose template here:
    <node_data>
    	<x>220</x>
    	<y>292</y>
    	<node_label>Right Brow Inner</node_label>
    	<lmb_horiz_prop>ytran</lmb_horiz_prop>
    	<lmb_vert_prop>xtran</lmb_vert_prop>
    	<rmb_horiz_prop>zrot</rmb_horiz_prop>
    	<rmb_horiz_sign>neg</rmb_horiz_sign>
    	<rmb_vert_prop>xrot</rmb_vert_prop>
    </node_data>
    <node_data>
    ...
    </node_data>
    #finish the template file with:
    </template_file>

     

    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.

        <tplset>
            <name>Male</name>
    		<dynamics_file>bipedDynamics.dsx</dynamics_file>
            <geometry>
                <name>Mike3</name>
                <name>Apollo Maximus</name>

    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.

    <template_suite>
        <title>PowerPose Templates</title>
        <default_set>Generic</default_set>
        <default_label>Body</default_label>
        <tplset>
            <name>Female</name>
    		<dynamics_file>bipedDynamics.dsx</dynamics_file>
            <geometry>
                <name>blMilWom_v4b</name>
                <name>blMilWom_v4</name>
                <name>blMilWom_v3</name>
                <name>blAiko3</name>
                <name>blSkeleton_v3</name>
                <name>blStephaniePetite</name>
                <name>blYTGirl</name>
                <name>sheFreak</name>
            </geometry>
            <template>
                <label>Body</label>
                <file>femBody.dsx</file>
            </template>
            <template>
                <label>Hands</label>
                <file>femHands.dsx</file>
            </template>
            <template>
                <label>Head</label>
                <file>femHead.dsx</file>
            </template>
        </tplset>
        
        <tplset>
            <name>Male</name>
    		<dynamics_file>bipedDynamics.dsx</dynamics_file>
            <geometry>
                <name>blMilMan_m4b</name>
                <name>blMilMan_m3</name>
                <name>blDavid</name>
                <name>blFREAK</name>
                <name>blSkeleton_m3</name>
                <name>blHiro</name>
                <name>blYTBoy</name>
                <name>blDemon</name>
                <name>blBaby</name>
            </geometry>
            <template>
                <label>Body</label>
                <file>maleBody.dsx</file>
            </template>
            <template>
                <label>Hands</label>
                <file>maleHands.dsx</file>
            </template>
            <template>
                <label>Head</label>
                <file>maleHead.dsx</file>
            </template>
        </tplset>
        
        <tplset>
            <name>Generic</name>
    		<dynamics_file>bipedDynamics.dsx</dynamics_file>
            <geometry/>
            <template>
                <label>Body</label>
                <file>genBody.dsx</file>
            </template>
            <template>
                <label>Hands</label>
                <file>genHands.dsx</file>
            </template>
            <template>
                <label>Head</label>
                <file>genHead.dsx</file>
            </template>
        </tplset>
    
        <tplset>
            <name>Dragon</name>
    		<dynamics_file>dragonDynamics.dsx</dynamics_file>
            <geometry>
                <name>blMilDragon2</name>
                <name>blMilDragon</name>
            </geometry>
            <template>
                <label>Head</label>
                <file>dragonHead.dsx</file>
            </template>
            <template>
                <label>Tail</label>
                <file>dragonTail.dsx</file>
            </template>
            <template>
                <label>Body</label>
                <file>dragonBody.dsx</file>
            </template>
            <template>
                <label>Wings</label>
                <file>dragonWings.dsx</file>
            </template>
            <template>
                <label>Claws</label>
                <file>dragonClaws.dsx</file>
            </template>
        </tplset>
       
    	<tplset>
    		<name>Genesis</name>
    		<dynamics_file>bipedDynamics.dsx</dynamics_file>
    		<geometry>
    			<name>Genesis</name>
    			</geometry>
    		<template>
    			<label>Body</label>
    			<file>genesisBody.dsx</file>
    		</template>
    		<template>
    			<label>Hands</label>
    			<file>genesisHands.dsx</file>
    		</template>
    		<template>
    			<label>Head</label>
    			<file>genesisHead.dsx</file>
    		</template>
    	</tplset>
    
    	<tplset>
    		<name>Genesis 2 Female</name>
    		<dynamics_file>bipedDynamics.dsx</dynamics_file>
    		<geometry>
    			<name>Genesis2Female</name>
    		</geometry>
    		<template>
    			<label>Body</label>
    			<file>G2F-Body.dsx</file>
    		</template>
    		<template>
    			<label>Hands</label>
    			<file>G2F-Hands.dsx</file>
    		</template>
    		<template>
    			<label>Head</label>
    			<file>G2F-Head.dsx</file>
    		</template>
    	</tplset>
    		
    	<tplset>
    		<name>Genesis 2 Male</name>
    		<dynamics_file>bipedDynamics.dsx</dynamics_file>
    		<geometry>
    			<name>Genesis2Male</name>
    		</geometry>
    		<template>
    			<label>Body</label>
    			<file>G2M-Body.dsx</file>
    		</template>
    		<template>
    			<label>Hands</label>
    			<file>G2M-Hands.dsx</file>
    		</template>
    		<template>
    			<label>Head</label>
    			<file>G2M-Head.dsx</file>
    		</template>
    	</tplset>  
    
    	<tplset>
    		<name>MerTail</name>
    		<dynamics_file>bipedDynamics.dsx</dynamics_file>
    		<geometry>
    			<name>MerTail</name>
    		</geometry>
    		<template>
    			<label>Body</label>
    			<file>MerTail.dsx</file>
    		</template>
    	</tplset> 
    
    	<tplset>
    		<name>SnakeTail</name>
    		<dynamics_file>bipedDynamics.dsx</dynamics_file>
    		<geometry>
    			<name>SnakeTail</name>
    		</geometry>
    		<template>
    			<label>Body</label>
    			<file>Snake Tail.dsx</file>
    		</template>
    	</tplset> 
    
    	<tplset>
    		<name>SuccubusWings</name>
    		<dynamics_file>bipedDynamics.dsx</dynamics_file>
    		<geometry>
    			<name>SuccubusWings</name>
    		</geometry>
    		<template>
    			<label>Body</label>
    			<file>Succubus Wings.dsx</file>
    		</template>
    	</tplset> 
    
    </template_suite>

     

    Post edited by Syrus_Dante on
Sign In or Register to comment.