User Tools

Site Tools


Save a Hierarchical Material(s) Preset

Summary

Below is an example demonstrating how you can use script accessible settings to control the saving of a Hierarchical Material(s) Preset..., without causing the output options or file save dialogs to be displayed.

API Areas of Interest

Example

Save_Hierarchical_Materials_Preset.dsa
// DAZ Studio version 4.9.3.121 filetype DAZ Script
 
// Define an anonymous function;
// serves as our main loop,
// limits the scope of variables
(function(){
 
	// Initialize whether or not property sub-items are supported
	var g_bSupportsNodeSubItems = false;
	var g_bSupportsMaterialSubItems = false;
	var g_bSupportsNameAsKey = true;
 
	// Initialize 'static' variables that hold modifier key state
	var s_bShiftPressed = false;
	var s_bControlPressed = false;
	var s_bAltPressed = false;
	var s_bMetaPressed = false;
 
	// If the "Action" global transient is defined, and its the correct type
	if( typeof( Action ) != "undefined" && Action.inherits( "DzScriptAction" ) ){
		// If the current key sequence for the action is not pressed
		if( !App.isKeySequenceDown( Action.shortcut ) ){
			updateModifierKeyState();
		}
	// If the "Action" global transient is not defined
	} else if( typeof( Action ) == "undefined" ) {
		updateModifierKeyState();
	}
 
	/*********************************************************************/
	// void : A function for updating the keyboard modifier state
	function updateModifierKeyState()
	{
		// Get the current modifier key state
		var nModifierState = App.modifierKeyState();
		// Update variables that hold modifier key state
		s_bShiftPressed = (nModifierState & 0x02000000) != 0;
		s_bControlPressed = (nModifierState & 0x04000000) != 0;
		s_bAltPressed = (nModifierState & 0x08000000) != 0;
		s_bMetaPressed = (nModifierState & 0x10000000) != 0;
	};
 
	/*********************************************************************/
	// void : A function for printing only if debugging
	function debug()
	{
		// If we are not debugging
		if( !s_bAltPressed ){
			// We are done...
			return;
		}
 
		// Convert the arguments object into an array
		var aArguments = [].slice.call( arguments );
 
		// Print the array
		print( aArguments.join(" ") );
	};
 
	/*********************************************************************/
	// 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;
	};
 
	/*********************************************************************/
	// Boolean : A function for testing whether or not a QObject instance
	// inherits one of a list of types
	function inheritsType( oObject, aTypeNames )
	{
		// If the object does not define the 'inherits' function
		if( !oObject || typeof( oObject.inherits ) != "function" ){
			// We are done... it is not a QObject
			return false;
		}
 
		// Iterate over the list of type names
		for( var i = 0, nTypes = aTypeNames.length; i < nTypes; i += 1 ){
			// If the object does not inherit the 'current' type
			if( !oObject.inherits( aTypeNames[i] ) ){
				// Next!!
				continue;
			}
 
			// Return the result
			return true;
		}
 
		// Return the result
		return false;
	};
 
	/*********************************************************************/
	// 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 is not passed in
		if( !oGroup ){
			// We are 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.0.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 );
	};
 
	/*********************************************************************/
	// Array<DzProperty> : A function for getting the list properties for an element
	function getElementPropertiesInPath( oElement, sPath, bRecurse )
	{
		// If the path is empty
		if( !sPath ){
			// Return the properties for the element
			return getElementProperties( oElement, true, bRecurse );
		}
 
		// Initialize
		var oGroup = oElement.getPropertyGroups();
		var sName = "";
		var nIdx = -1;
		var sSubPath = sPath;
 
		// While the remaining path is not empty
		while( oGroup && !sSubPath.isEmpty() ){
			// Get the index of the first slash
			nIdx = sSubPath.indexOf( "/" );
			// If a slash was not found
			if( nIdx < 0 ){
				// The group name is the path
				sName = sSubPath;
				// Break the loop on the next evaluation
				sSubPath = "";
			// If a slash was found
			} else {
				// The group name is before the slash
				sName = sSubPath.left( nIdx );
				// Get the remaining path
				sSubPath = sSubPath.right( sSubPath.length - nIdx - 1 );
			}
 
			// Get the sub group
			oGroup = oGroup ? oGroup.findChild( sName ) : null;
		}
 
		// Return the properties for the element
		return getGroupProperties( oGroup, false, bRecurse );
	};
 
	/*********************************************************************/
	// DzNode : A function for getting the root of a node
	function getRootNode( oNode )
	{
		// If we have a node and it is a bone
		if( oNode && inheritsType( oNode, ["DzBone"] ) ){
			// We want the skeleton
			return oNode.getSkeleton();
		}
 
		// Return the original node
		return oNode;
	};
 
	/*********************************************************************/
	// void : A function for setting the default options
	function setDefaultOptions( oSettings, sRootLabel )
	{
		// If the root label is not empty
		if( !sRootLabel.isEmpty() ){
			// Set the label of the root node to find it in the scene;
			// this can be used to override selection within the scene
			oSettings.setStringValue( "RootLabel", sRootLabel );
		}
	};
 
	/*********************************************************************/
	// void : A function for setting the property options for an element
	function setElementPropertyOptions( oSettings, sElementSettingsKey, sElementName, aPropNames, bSubItems, bNameAsKey )
	{
		// Get the (nested) settings that hold the named element and properties
		var oElementsSettings = oSettings.getSettingsValue( sElementSettingsKey );
		// If the object doesn't already exist
		if( !oElementsSettings ){
			// Create it
			oElementsSettings = oSettings.setSettingsValue( sElementSettingsKey );
		}
 
		// Get the (nested) settings object for the element
		var oElementSettings = oElementsSettings.getSettingsValue( sElementName );
		// If the object doesn't already exist
		if( !oElementSettings ){
			// Create it
			oElementSettings = oElementsSettings.setSettingsValue( sElementName );
		}
 
		// Declare working variable
		var vPropertyItem;
		var sPropertyName;
		var oPropertySettings, oSubItemSettings;
 
		// Iterate over the property items
		for( var i = 0; i < aPropNames.length; i += 1 ){
			// Get the 'current' property item
			vPropertyItem = aPropNames[ i ];
 
			// If the filter doesn't support property sub-items,
			// or the property item is a string
			if( !bSubItems || typeof( vPropertyItem ) == "string" ){
				// Set the property name to the item
				sPropertyName = vPropertyItem;
 
				// Add a setting wherein the key is the index in the list
				// and the value is the property name
				oElementSettings.setStringValue( String( i ), sPropertyName );
			// If the property item is an array
			} else if( typeof( vPropertyItem ) == "object" && Array.isArray( vPropertyItem ) ){
				// Set the property name to the first item, which should be a string
				sPropertyName = (vPropertyItem.length > 0 &&
					typeof( vPropertyItem[ 0 ] ) == "string" ? vPropertyItem[ 0 ] : "");
				// If the name was not set
				if( sPropertyName.isEmpty() ){
					// Next!!
					continue;
				}
 
				if( bNameAsKey ){
					// Get the (nested) settings object for the property
					oPropertySettings = oElementSettings.getSettingsValue( sPropertyName );
					// If the object doesn't already exist
					if( !oPropertySettings ){
						// Create it
						oPropertySettings = oElementSettings.setSettingsValue( sPropertyName );
					}
 
					// Iterate over the items, skippping the first element
					for( var j = 1; j < vPropertyItem.length; j += 1 ){
						// Set a named key to the value
						oPropertySettings.setStringValue( String( j - 1 ), vPropertyItem[ j ] )
					}
				} else {
					// Get the (nested) settings object for the property
					oPropertySettings = oElementSettings.getSettingsValue( i );
					// If the object doesn't already exist
					if( !oPropertySettings ){
						// Create it
						oPropertySettings = oElementSettings.setSettingsValue( i );
					}
 
					// Set the name setting
					oPropertySettings.setStringValue( "name", sPropertyName );
					// Get the (nested) settings object for the subitems
					oSubItemSettings = oPropertySettings.getSettingsValue( "subitems" );
					// If the object doesn't already exist
					if( !oSubItemSettings ){
						// Create it
						oSubItemSettings = oPropertySettings.setSettingsValue( "subitems" );
					}
 
					// Iterate over the items, skippping the first element
					for( var j = 1; j < vPropertyItem.length; j += 1 ){
						// Set an index key to the value
						oSubItemSettings.setStringValue( String( j - 1 ), vPropertyItem[ j ] )
					}
				}
			}
		}
	};
 
	/*********************************************************************/
	// void : A function for setting the property options for a material on a node
	function setNodeMaterialPropertyOptions( oSettings, sNodeName, sMaterialName, aPropNames )
	{
		// Get the (nested) settings that hold the named element and properties
		var oNodeNameSettings = oSettings.getSettingsValue( "NodeNames" );
		// If the object doesn't already exist
		if( !oNodeNameSettings ){
			// Create it
			oNodeNameSettings = oSettings.setSettingsValue( "NodeNames" );
		}
 
		// Set the property options for the named node
		setElementPropertyOptions( oNodeNameSettings, sNodeName, sMaterialName, aPropNames, g_bSupportsMaterialSubItems, g_bSupportsNameAsKey );
	};
 
	/*********************************************************************/
	// void : A function for setting common property options for multiple materials on multiple nodes
	function setCommonNodeMaterialPropertyOptions( oSettings, aNodeLabels, aMaterialNames, aPropNames )
	{
		// Iterate over the material names array
		for( var i = 0; i < aNodeLabels.length; i += 1 ){
			// Iterate over the material names array
			for( var j = 0; j < aMaterialNames.length; j += 1 ){
				// Set the property options for the 'current' material name
				setNodeMaterialPropertyOptions( oSettings, aNodeLabels[ i ], aMaterialNames[ j ], aPropNames );
			}
		}
	};
 
	/*********************************************************************/
	// void : A function for adding an element name
	function setElementOption( oSettings, sElementSettingsKey, sElementName )
	{
		// Get the (nested) settings that hold the named element
		var oElementsSettings = oSettings.getSettingsValue( sElementSettingsKey );
		// If the object doesn't already exist
		if( !oElementsSettings ){
			// Create it
			oElementsSettings = oSettings.setSettingsValue( sElementSettingsKey );
		}
 
		// Declare working variable
		var sElement;
 
		// Initialize
		var i = 0;
 
		// Iterate over the element names
		for( nElements = oElementsSettings.getNumValues(); i < nElements; i += 1 ){
			// Get the 'current' element name
			sElement = oElementsSettings.getValue( i );
			// If the name is the same as the one we are adding
			if( sElement == sElementName ){
				// We are done...
				return;
			}
		}
 
		// Create it
		oElementSettings = oElementsSettings.setStringValue( String( i ), sElementName );
	};
 
	/*********************************************************************/
	// void : A function for adding a node name
	function setNodeOption( oSettings, sNodeName )
	{
		// Set the property options for the named node
		setElementOption( oSettings, "NodeNames", sNodeName );
	};
 
	/*********************************************************************/
	// void : A function for adding multiple nodes names
	function setNodeOptions( oSettings, aNodeNames )
	{
		// Iterate over the node names array
		for( var i = 0; i < aNodeNames.length; i += 1 ){
			// Set the property options for the 'current' node name
			setNodeOption( oSettings, aNodeNames[ i ] );
		}
	};
 
	/*********************************************************************/
	// void : A function for setting the required options
	function setRequiredOptions( oSettings, bShowOptions )
	{
		// If we are not showing options
		if( !bShowOptions ){
			// Remove settings that have no meaning
			oSettings.removeValue( "IncludeSimulationSettings" );
		}
 
		// Set the initial state of the compress file checkbox
		oSettings.setBoolValue( "CompressOutput", false );
 
		// Do not to show the options
		oSettings.setBoolValue( "RunSilent", !bShowOptions );
	};
 
	/*********************************************************************/
	// Array : A function for getting an array of node labels
	function getNodeLabels( oNode, bRecurse, bSkeletonPrefix )
	{
		// If we do not have a node
		if( !oNode ){
			// We are done...
			return [];
		}
 
		// Intialize the array with the name of the node passed in
		var aNames;
 
		// If we are prepending the skeleton label
		if( bSkeletonPrefix ){
			// Get the skeleton for the node
			var oSkeleton = oNode.getSkeleton();
			// If there is no skeleton or the node is a skeleton
			if( !oSkeleton || pointersAreEqual( oSkeleton, oNode ) ){
				// Initialize with the node label
				aNames = [ oNode.getLabel() ];
			// If the node is within the hierarchy of a skeleton,
			// but the node is not a skeleton
			} else {
				// Initialize with the skeleton label prepended
				aNames = [ String( "%1::%2" )
							.arg( oSkeleton.getLabel() )
							.arg( oNode.getLabel() )
						];
			}
		// If the application version is older
		} else {
			// Initialize with the node label;
			// prepending the skeleton label is not supported
			aNames = [ oNode.getLabel() ];
		}
 
		// If we are recursing
		if( bRecurse ){
			// Iterate over the node's children
			for( var i = 0, nNodes = oNode.getNumNodeChildren(); i < nNodes; i += 1 ){
				// Concatenate the names of the child nodes, recursively
				aNames = aNames.concat( getNodeLabels( oNode.getNodeChild( i ), true, bSkeletonPrefix ) );
			}
		}
 
		// Return the names
		return aNames;
	};
 
	/*********************************************************************/
	// String || Array<String,String> : A function for getting the property entry
	function getPropertyEntry( oProperty, bIncludeValue, bIncludeImage )
	{
		// If the property is numeric and mappable
		if( inheritsType( oProperty, ["DzNumericProperty"] ) && oProperty.isMappable() ){
			// If we are including value and image
			if( bIncludeValue == bIncludeImage ){
				// Use the label
				return oProperty.getLabel();
			// If we are only including the value
			} else if( bIncludeValue ){
				// Use the label and Value keyword
				return [ oProperty.getLabel(), "Value" ];
			// If we are only including the image
			} else if( bIncludeImage ){
				// Use the label and Image keyword
				return [ oProperty.getLabel(), "Image" ];
			} else {
				return undefined;
			}
		}
 
		// Use the label
		return oProperty.getLabel();
	};
 
	/*********************************************************************/
	// Boolean : A function for getting whether a node is in the hierarchy of another node
	function inNodeHierarchy( oBaseNode, oNode, bRequireShape )
	{
		// Initialize
		var oParent = oNode;
 
		// If we are requiring a shape
		if( bRequireShape ){
			// Get the object
			var oObject = oNode.getObject();
			// If we do not have an object
			if( !oObject ){
				// We are done...
				return false;
			}
 
			// Get the current shape
			var oShape = oObject.getCurrentShape();
			// If we do not have a shape
			if( !oShape ){
				// We are done...
				return false;
			}
		}
 
		// While we have a node
		while ( oParent ){
			// If the node is the same as the base node
			if ( pointersAreEqual( oParent, oBaseNode ) ){
				// We've found what we are looking for
				return true;
			}
 
			// Climb the hierarchy
			oParent = oParent.getNodeParent();
		}
 
		// We did not find what we are looking for
		return false;
	};
 
	/*********************************************************************/
	// Get the asset IO manager
	var oAssetIOMgr = App.getAssetIOMgr();
	// Define the class name of the asset filter we want to use
	var sClassName = "DzHierarchicalMaterialAssetFilter";
	// Find the index of the asset filter with the class name we want
	var nAssetIOFilter = oAssetIOMgr.findFilter( sClassName );
	// If we did not find an asset filter with the class name we wanted
	if( nAssetIOFilter < 0 ){
		// Inform the user
		MessageBox.critical( text( "An asset filter with the class name " +
			"\"%1\" could not be found.").arg( sClassName ),
			text( "Critical Error" ), text( "&OK" ) );
 
		// We are done...
		return;
	}
 
	// Get the asset filter at the prescribed index
	var oAssetIOFilter = oAssetIOMgr.getFilter( nAssetIOFilter );
	// If we do not have a valid asset filter
	if( !oAssetIOFilter ){
		// Inform the user
		MessageBox.critical( text( "An asset filter with the class name " +
			"\"%1\" could not be found.").arg( sClassName ),
			text( "Critical Error" ), text( "&OK" ) );
 
		// We are done...
		return;
	}
 
	// Create a settings object
	var oSettings = new DzFileIOSettings();
 
	// Get the default settings
	oAssetIOFilter.getDefaultOptions( oSettings );
 
	// Define whether or not to show options
	var bShowOptions = s_bControlPressed;
	var bOptionsShown = false;
 
	// Get the root of the primary selection
	var oRootNode = getRootNode( Scene.getPrimarySelection() );
 
	// If we had a node selected, get its name otherwise use a default
	var sRootName = (oRootNode ? oRootNode.getName() : "Genesis8Female");
	var sRootLabel = (oRootNode ? oRootNode.getLabel() : "");
 
	// Get the content manager
	var oContentMgr = App.getContentMgr();
 
	// Get the base path - the first mapped content directory
	var sBasePath = oContentMgr.getContentDirectoryPath( 0 );
 
	// Set the default options; this can be used to set
	// options before the dialog is displayed
	setDefaultOptions( oSettings, sRootLabel );
 
	// Debug
	debug( "Defaults:", oSettings.toJsonString() );
 
	// If we are showing options, we can override the last saved state
	// by passing in the settings we want to override;
	// if we cannot get the default/saved options for the asset filter,
	// without displaying the options dialog
	if( !oAssetIOFilter.getOptions( oSettings, bShowOptions, "" ) ){
		// We are done...
		return;
	// If we can get the options for the importer
	} else {
		// Capture that options were shown
		bOptionsShown = true;
 
		// Debug
		debug( "Get:", oSettings.toJsonString() );
	}
 
	// Define whether or not the the root (skeleton) is allowed in the preset
	var bAllowRoot = true;
 
	// Initialize
	var aNodeLabels = [ sRootLabel ];
	var aSurfaceNames = [ "Default" ];
	var aPropertyNames = [ "Tags", ["Diffuse Color", "Value"] ];
 
	// If the application version is 4.10.0.111 or newer
	var bSkeletonPrefix = App.version64 >= 0x0004000a0000006f;
 
	// Initialize an array for collecting nodes
	var aNodes = [];
 
	// If we are not showing options and we had a node selected
	if( !bShowOptions && oRootNode ){
		// The material asset filter supports property sub-items;
		// if the property name is specified alone, as a string,
		// it will cause the Value and Image data for that property
		// to be saved to the preset; if the property element is
		// specified in an array of strings, where the first item
		// is the property name followed by "Value" and/or "Image",
		// then only the specified data for that property will be
		// saved to the preset
		g_bSupportsMaterialSubItems = true;
 
		// Define whether we only want the selected nodes (false),
		// or whether we want selected recursive (true)
		var bRecursive = false;
 
		// Declare working variables
		var oNode, oParent;
		var bAddNode;
 
		// Create a array helper object
		var oArrayHelper = new DzArrayHelper();
 
		// Get the selected nodes from the scene
		var aSelectedNodes = Scene.getSelectedNodeList();
		// Iterate over the selected nodes
		for( var i = 0, nNodes = aSelectedNodes.length; i < nNodes; i += 1 ){
			// Get the 'current' node
			oNode = aSelectedNodes[ i ];
 
			// If the node is the root of the primary selection and we are not allowing the root
			if( pointersAreEqual( oRootNode, oNode ) && !bAllowRoot ){
				// Next!!
				continue;
			}
 
			// If the node is not in the hierarchy of the primary root
			if( !inNodeHierarchy( oRootNode, oNode, true ) ){
				// Next!!
				continue;
			}
 
			// Initialize
			bAddNode = true;
 
			// If we are recursing
			if( bRecursive ){
				// Get the node parent
				oParent = oNode.getNodeParent();
				// Climb the node hierarchy
				while( oParent ){
					// If the parent is selected
					if( oParent.isSelected() ){
						// Update the flag
						bAddNode = false;
						// We are done climbing...
						break;
					}
 
					// Get the next higher ancestor
					oParent = oParent.getNodeParent();
				}
			}
 
			// If we are not adding the node
			if( !bAddNode ){
				// Next!!
				continue;
			}
 
			// Add the the node to the array; if it is not already there
			aNodes = oArrayHelper.addToArray( aNodes, oNode );
		}
 
		// Clear
		aNodeLabels = [];
 
		// Iterate over the nodes
		for( var i = 0; i < aNodes.length; i += 1 ){
			// Collect node labels
			aNodeLabels = aNodeLabels.concat( getNodeLabels( aNodes[ i ], bRecursive, bSkeletonPrefix ) );
		}
	}
 
	// If we are not showing options
	if( !bShowOptions ){
		// If the application version is 4.10.0.111 or newer
		if( App.version64 >= 0x0004000a0000006f ){
			// Set common property options for multiple nodes
			//setCommonNodeMaterialPropertyOptions( oSettings, aNodeLabels, aSurfaceNames, aPropertyNames );
 
			// Declare working variables
			var oNode, oObject, oShape, oMaterial;
			var aMaterials;
			var nMaterials;
 
			// Iterate over the nodes
			for( var i = 0; i < aNodes.length; i += 1 ){
				// Get the 'current' node
				oNode = aNodes[ i ];
				// Get the object
				oObject = oNode.getObject();
				// Get the current shape
				oShape = oObject.getCurrentShape();
 
				// Get the selected materials
				aMaterials = oShape.getAllSelectedMaterials();
				nMaterials = aMaterials.length;
				// If we do not have selected materials
				if( nMaterials < 1 ){
					// Get all materials
					aMaterials = oShape.getAllMaterials();
					nMaterials = aMaterials.length;
				}
 
				// Iterate over the materials
				for( var j = 0; j < nMaterials; j += 1 ){
					// Get the 'current' material
					oMaterial = aMaterials[ j ];
 
					// Get the node labels; do not recurse; should only be one
					aNodeLabels = getNodeLabels( oNode, false, bSkeletonPrefix );
					// If we have 1 label
					if( aNodeLabels.length == 1 ){
						// Set specific property options for the node material
						setNodeMaterialPropertyOptions( oSettings, aNodeLabels[ 0 ], oMaterial.name, aPropertyNames );
					}
				}
			}
		// If the application version is older
		} else {
			// Get the number of figures fit to the primary selection
			var nFollowers = oRootNode.getNumFollowSkeletons();
			// Pre-size an array of node labels
			var aNodeLabels = new Array( nFollowers );
			// Iterate over the followers
			for( var i = 0; i < nFollowers; i += 1 ){
				// Insert the label
				aNodeLabels[ i ] = oRootNode.getFollowSkeleton( i ).getLabel();
			}
			// Pre-pend the root node's label
			aNodeLabels.unshift( sRootLabel );
 
			// Set the nodes options
			setNodeOptions( oSettings, aNodeLabels );
		}
	}
 
	// Set the required options; override user settings if needed
	setRequiredOptions( oSettings, !bOptionsShown );
 
	// Debug
	debug( "Required:", oSettings.toJsonString() );
 
	// Construct the name of the file to save to; omit file extension
	var sFile = String("%1/%2 Test").arg( sBasePath ).arg( sClassName );
 
	// Use the asset manager to save a file, using the filter and defined settings
	var oError = oAssetIOMgr.doSaveWithOptions( oAssetIOFilter, oSettings,
		false, sFile, sBasePath, "" );
 
	// If there was no error
	if( oError.valueOf() == 0x00000000 ){
		// Debug
		debug( "Saved:", sFile );
	// If there was an error
	} else {
		// Debug
		debug( "Error:", getErrorMessage( oError ) );
	}
 
	// Clean up; do not leak memory
	oAssetIOFilter.deleteLater();
 
// Finalize the function and invoke
})();