User Tools

Site Tools


Post-Load Material Proxy Create

Summary

Below is an example demonstrating how you can create proxy properties on a node for a given set of properties on a surface, and link the properties on the surface to the corresponding properties on the node, so that the surface properties can be animated. The example also demonstrates how to embed post-load data on the node and cause a script to be executed after the node has been loaded into the scene in order to re-establish the links between the properties which are not otherwise saved.

See Also: Post-Load Material Proxy Link Properties

API Areas of Interest

Example

Post_Load_Material_Proxy_Create.dsa
// DAZ Studio version 4.10.0.123 filetype DAZ Script
 
// Define an anonymous function;
// serves as our main loop,
// limits the scope of variables
(function(){
 
	/*********************************************************************/
	// String : A function for retrieving a translation if one exists
	function text( sText )
	{
		// If the version of the application supports qsTr()
		if( typeof( qsTr ) != "undefined" ){
			// Return the translated (if any) text
			return qsTr( sText );
		}
 
		// Return the original text
		return sText;
	};
 
	/*********************************************************************/
	// Array<DzProperty> : A function for getting a list of the properties in a group
	function getGroupProperties( oGroup, bTraverse, bRecurse )
	{
		// Declare an array to hold properties
		var aProperties = [];
 
		// If a group isn't passed in
		if( !oGroup ){
			// We're done, return an empty array
			return aProperties;
		}
 
		// Get the number of proeprties in the group
		var nProperties = oGroup.getNumProperties();
		// Pre-size the properties array
		aProperties = new Array( nProperties );
		// Iterate over the properties, setting each element in the array
		for( var i = 0; i < nProperties; i += 1 ){
			// Assign the property to the position in the array
			aProperties[ i ] = oGroup.getProperty( i );
		}
 
		// If we are recursing
		if( bRecurse ){
			// Concatenate the properties array from child groups
			aProperties = aProperties.concat(
				getGroupProperties( oGroup.getFirstChild(), true, bRecurse ) );
		}
 
		// If we are traversing
		if( bTraverse ){
			// Concatenate the properties array from sibling groups
			aProperties = aProperties.concat(
				getGroupProperties( oGroup.getNextSibling(), bTraverse, bRecurse ) );
		}
 
		// Return the array of properties
		return aProperties;
	};
 
	/*********************************************************************/
	// Array<DzProperty> : A function for getting the list properties for an element
	function getElementProperties( oElement, bTraverse, bRecurse )
	{
		// Get the property group tree for the element
		var oPropertyGroupTree = oElement.getPropertyGroups();
 
		// If the application version is 4.9.4.101 or newer and we want all properties
		if( App.version64 >= 0x0004000900040065 && bTraverse && bRecurse ){
			// Return the properties for the element
			return oPropertyGroupTree.getAllProperties();
		}
 
		// Get the first group in the tree
		var oPropertyGroup = oPropertyGroupTree.getFirstChild();
		// Return the properties for the element
		return getGroupProperties( oPropertyGroup, bTraverse, bRecurse );
	};
 
	/*********************************************************************/
	// DzProperty : A function for finding a property associated with an element
	function findElementProperty( oElement, sProperty, bUseLabel )
	{
		// Whether or not to use optimizations; 4.7.1.44 or newer
		var bUseOptimization = (App.version64 >= 0x000400070001002c);
 
		// Declare a working variable
		var oProperty;
 
		// If the application version is 4.7.1.44 or newer and we're not using
		// the label to find, or the application version is 4.11.0.166 or newer
		if( (bUseOptimization && !bUseLabel) ||
			App.version64 >= 0x0004000b000000a6 ){
			// Get the property group tree for the element
			var oPropertyGroupTree = oElement.getPropertyGroups();
 
			// If we're using the label
			if( bUseLabel ){
				// Attempt to find the property
				oProperty = oPropertyGroupTree.findPropertyByLabel( sProperty );
			// If we're not using the label
			} else {
				// Attempt to find the property
				oProperty = oPropertyGroupTree.findProperty( sProperty );
			}
 
			// If we found a property
			if( oProperty ){
				// We're done, return it
				return oProperty;
			}
		// Otherwise
		} else {
			// Get the properties of the element
			var aProperties = getElementProperties( oElement, true, true );
			// Iterate over the properties
			for( var i = 0; i < aProperties.length; i += 1 ){
				// Get the 'current' property
				oProperty = aProperties[i];
 
				// If we're using the label
				if( bUseLabel ){
					// If the label of the property is the one we're looking for
					if( oProperty.getLabel() == sProperty ){
						// We're done, return it
						return oProperty;
					}
				// If we're not using the label
				} else {
					// If the name of the property is the one we're looking for
					if( oProperty.name == sProperty ){
						// We're done, return it
						return oProperty;
					}				
				}
			}
		}
 
		return null;
	};
 
	/*********************************************************************/
	// DzNode : A function for getting the root of a node
	function getRootNode( oNode )
	{
		// If a node is selected and it is a bone
		if( oNode && oNode.inherits( "DzBone" ) ){
			// We want the skeleton
			return oNode.getSkeleton();
		}
 
		// Return the original node
		return oNode;
	};
 
	/*********************************************************************/
	// String : A function for finding an absolute file path of a relative one
	function findAbsContentFilePath( sRelFilePath )
	{
		// If the relative path is empty
		if( sRelFilePath.isEmpty() ){
			// We're done...
			return "";
		}
 
		// Get the content manager
		var oContentMgr = App.getContentMgr();
 
		// Declare working variable
		var sFilePath;
 
		// Initialize
		var bHasAllEnums = (App.version64 >= 0x000400090000002e);//4.9.0.46
		var nDirType = DzContentMgr.AllDirs;
 
		// Get the import manager
		var oImportMgr = App.getImportMgr();
		// If the file type is imported
		if( oImportMgr.findImporter( sRelFilePath ) ){
			// If the version does not provide all enumerated values
			if( !bHasAllEnums ){
				// Update the directory type
				nDirType = DzContentMgr.PoserDirs |
							DzContentMgr.ImportDirs |
							0x20; //DzContentMgr.CloudDB
			// Otherwise
			} else {
				// Update the directory type
				nDirType = DzContentMgr.PoserDirs |
							DzContentMgr.ImportDirs |
							DzContentMgr.CloudDB;
			}
		// If the file type is native
		} else {
			// If the version does not provide all enumerated values
			if( !bHasAllEnums ){
				// Update the directory type
				nDirType = DzContentMgr.NativeDirs |
							0x20; //DzContentMgr.CloudDB
			// Otherwise
			} else {
				// Update the directory type
				nDirType = DzContentMgr.NativeDirs |
							DzContentMgr.CloudDB;
			}
		}
 
		// Return the path
		return oContentMgr.findFile( sRelFilePath, nDirType );
	};
 
	/*********************************************************************/
	// String : A function for retrieving the name of the vendor
	function getVendorName( sOverride )
	{
		// Set the vendor to the registered author
		var sVendor = App.getCurrentAuthor().name;
		// If a name was specified
		if( !sOverride.isEmpty() ){
			// Use the specified name
			sVendor = sOverride;
		}
 
		// If we still don't have a name
		if( sVendor.isEmpty() ){
			// Use a default
			sVendor = "Vendor Name";
		}
 
		// Return the vendor name
		return sVendor;
	};
 
	/*********************************************************************/
	// String : A function for retrieving a name for a product
	function getProductName( sDefault )
	{
		// Initialize
		var sProductName = "Product Name";
 
		// Create a basic dialog
		var wDlg = new DzBasicDialog();
		wDlg.caption = text( sProductName );
 
		// Add a label
		var wLbl = new DzLabel( wDlg );
		wLbl.text = text( sProductName ) + ":";
		wDlg.addWidget( wLbl );
 
		// Add a line edit
		var wNameLEdit = new DzLineEdit( wDlg );
		wNameLEdit.text = sDefault;
		wDlg.addWidget( wNameLEdit );
 
		// Get the wrapped widget
		var oDlg = wDlg.getWidget();
 
		// Set the object name of the wrapped widget;
		// this is used for recording position and size
		oDlg.objectName = sProductName.replace( / /g, "" );
 
		// Get the minimum height
		var sizeHint = oDlg.minimumSizeHint;
		var nHeight = sizeHint.height;
 
		// Set the fixed height to the minimum
		wDlg.setFixedHeight( nHeight );		
 
		// If the user didn't cancel the dialog
		if( wDlg.exec() ){
			// Get the name specified
			var sUserInput = wNameLEdit.text;
			// If the input is valid
			if( !sUserInput.isEmpty() ){
				// Update the product name
				sProductName = sUserInput;
			}
		}
 
		// Return the product name
		return sProductName;
	};
 
	/*********************************************************************/
	// void : A function for copying the attribute of one property to another
	function copyPropertyAttributes( oSrcProperty, oTgtProperty, oSettings )
	{
		// Get the attributes of the source property
		oSrcProperty.getAttributes( oSettings );
 
		// Set the attributes of the target property
		oTgtProperty.setAttributes( oSettings );
 
		// Clean up
		oSettings.clear();
	};
 
	/*********************************************************************/
	// void : A function for creating a one to one link between properties
	function linkProperties( oSlaveProperty, oMasterProperty )
	{
		// Link the slave property to the master property
		oSlaveProperty.linkTo( oMasterProperty );
	};
 
	/*********************************************************************/
	// String : A function for establishing the post-load for linking properties
	function setLinkPropertiesPostLoad( oProxyElement, oTargetElement, aProperties )
	{
		// Get the name of the target element
		var sTargetName = oTargetElement.name;
 
		// Define a unique name for our data item
		var sDataName = String("%1__%2__%3__LinkPropertiesPostLoad")
					.arg( m_sVendorName )
					.arg( m_sProductName )
					.arg( sTargetName );
 
		// Get our data item
		var oDataItem = oProxyElement.findDataItem( sDataName );
 
		// If we didn't find our data item
		if( !oDataItem ){
			// Create our data item; we want it to be saved with the element
			oDataItem = new DzSimpleElementScriptData( sDataName, true );
 
			// Set the path to our script
			oDataItem.setScriptFilePath( m_sScriptRelPath );
 
			// Add the data item to the element
			oProxyElement.addDataItem( oDataItem );
		}
 
		// Get the settings for the data item
		var oSettings = oDataItem.getSettings();
 
		// Assign key/value pairs
		oSettings.setStringValue( "vendor", m_sVendorName );
		oSettings.setStringValue( "product", m_sProductName );
		oSettings.setStringValue( "target", sTargetName );
 
		// Get the properties settings
		var oPropertySettings = oSettings.getSettingsValue( "properties" );
		// If the properties settings does not exist
		if( !oPropertySettings ){
			// Create the properties settings
			oPropertySettings = new DzSettings();
		// If the properties settings does exist
		} else {
			// Clear the list of properties
			oPropertySettings.clear();
		}
 
		// Declare working variables
		var sProperty;
 
		// Iterate over the property names
		for( var i = 0, nProps = aProperties.length; i < nProps; i += 1 ){
			// Get the 'current' property name
			sProperty = aProperties[ i ];
 
			// Add the setting for the property
			oPropertySettings.setBoolValue( sProperty, true );
		}
 
		// Assign key/value pairs
		oSettings.setSettingsValue( "properties", oPropertySettings );
	};
 
	/*********************************************************************/
	// Define text variables for the message
	var sTitle = text( "Selection Error" );
	var sMessage = text( "A node with geometry must be selected to continue." );
	var sButton = text( "&OK" );
 
	// Get the primary selection
	var oNode = getRootNode( Scene.getPrimarySelection() );
	// If nothing is selected
	if( !oNode ){
		// Inform the user
		MessageBox.information( sMessage, sTitle, sButton );
 
		// We're done...
		return;
	}
 
	// Get the object for the node
	var oObject = oNode.getObject();
	// If we don't have an object
	if( !oObject ){
		// Inform the user
		MessageBox.information( sMessage, sTitle, sButton );
 
		// We're done...
		return;
	}
 
	// Get the current shape for the object
	var oShape = oObject.getCurrentShape();
	// If we don't have a shape
	if( !oShape ){
		// Inform the user
		MessageBox.information( sMessage, sTitle, sButton );
 
		// We're done...
		return;
	}
 
	// Get the vendor name
	var m_sVendorName = getVendorName( "" );
 
	// Get the product name
	var m_sProductName = getProductName( "" );
 
	// Define the post-load script name
	var m_sScriptName = "Post_Load_Material_Proxy_Link_Properties";
 
	// Define the script extension
	var sExtension = "dsa";
 
	// Construct the relative path of the post-load script;
	// by placing the script within the data folder, under a
	// vendor/product/item name for organizational purposes,
	// you have prepared it for product-ization and for being
	// installed via Daz Connect or Daz Install Manager
	var m_sScriptRelPath = String("data/%1/%2/%3.%4")
				.arg( m_sVendorName )
				.arg( m_sProductName )
				.arg( m_sScriptName )
				.arg( sExtension );
 
	// Find the post-load script within the content management system
	var sScriptAbsPath = findAbsContentFilePath( m_sScriptRelPath );	
	// If the script could not be found
	if( sScriptAbsPath.isEmpty() ){
		// Define text variables for the message
		sTitle = text( "Resource Error" );
		sMessage = text( "The '%1' file could not be found." )
				.arg( m_sScriptRelPath );
		// Inform the user
		MessageBox.information( sMessage, sTitle, sButton );
 
		// We're done...
		return;
	}
 
	// Define the list of properties on the
	// target material to create a proxy for
	var oSurfaceProperties = {
		"Default" : [
			"Diffuse Color",
			"Specular Color",
			"Ambient Color"
		]
	};
 
	// Create a settings object for copying property attributes
	var oSettings = new DzPropertySettings();
 
	// Declare working variables
	var sSurface, sProperty, sProxyName, sProxyPath;
	var oMaterial, oMaterialProperty, oProxyProperty;
	var aPropertyNames, aLinkProperties;
 
	// Get the list of surface names
	var aSurfaceNames = Object.keys( oSurfaceProperties );
	// Iterate over the surface names
	for( var i = 0; i < aSurfaceNames.length; i += 1 ){
		// Get the 'current' name
		sSurface = aSurfaceNames[ i ];
		// Find the material by name
		oMaterial = oShape.findMaterial( sSurface );
		// If we didn't find the material
		if( !oMaterial ){
			// Next!!
			continue;
		}
 
		// Initialize a list of properties
		aLinkProperties = [];
 
		// Get the list of property names for the surface
		aPropertyNames = oSurfaceProperties[ sSurface ];
		// Iterate over the property names
		for( var j = 0; j < aPropertyNames.length; j += 1 ){
			// Get the 'current' name
			sProperty = aPropertyNames[ j ];
			// Find the property by name
			oMaterialProperty = oMaterial.findProperty( sProperty );
			// If we didn't find the property
			if( !oMaterialProperty ){
				// Next!!
				continue;
			}
 
			// Construct the proxy property name
			sProxyName = String("%1 %2 Proxy").arg( oMaterial.name ).arg( sProperty );
			// Construct the proxy property path
			sProxyPath = String("Surfaces/%1/%2").arg( oMaterial.name ).arg( oMaterialProperty.getPath() );
 
			oProxyProperty = findElementProperty( oNode, sProxyName, false );
			if( !oProxyProperty ){
				// Duplicate the property
				oProxyProperty = oMaterialProperty.propertyDuplicate();
				oProxyProperty.name = sProxyName;
				oProxyProperty.setNew( true );
 
				// Add the property to the proxy node
				oNode.addProperty( oProxyProperty );
			}
 
			// Copy the setup of the material property to the proxy property
			copyPropertyAttributes( oMaterialProperty, oProxyProperty, oSettings );
 
			oProxyProperty.setPath( sProxyPath );
			oProxyProperty.setIsUserProperty( true );
 
			// If the property is numeric
			if( oMaterialProperty.inherits( "DzNumericProperty" ) ){
				// Link the material property to the proxy property
				linkProperties( oMaterialProperty, oProxyProperty );
 
				oProxyProperty.setCanAnimate( true );
 
				// Capture the name of the property
				aLinkProperties.push( oMaterialProperty.name );
			}
		}
 
		// If we've linked properties
		if( aLinkProperties.length > 0 ){
			// Set/Create the post-load that will re-establish the links
			setLinkPropertiesPostLoad( oNode, oMaterial, aLinkProperties );
		}
	}
 
// Finalize the function and invoke
})();