User Tools

Site Tools


List Products Used

Summary

Below is an example demonstrating how to obtain a list of products in the database used by the an object in the scene.

API Areas of Interest

Example

DB_List_Products_Used.dsa
// DAZ Studio version 4.9.2.27
// Define an anonymous function;
// serves as our main loop,
// limits the scope of variables
(function(){
 
	// 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 converting an ECMA Object to a JSON string
	function getStringified( oObject )
	{
		return JSON.stringify( oObject, null, "\t" );
	};
 
	/*********************************************************************/
	// Array : A function for getting the keys of an ECMA Object
	function getKeys( oObject )
	{
		return Object.keys( oObject );
	};
 
	/*********************************************************************/
	// Array : A function for getting the values of an ECMA Object
	function getValues( oObject )
	{
		return getKeys( oObject )
				.map( function( sProperty ){ return oObject[ sProperty ]; } );
	};
 
	/*********************************************************************/
	// Object : A function for creating a deep copy of an ECMA Object
	function deepCopy( oObject )
	{
		// Initialize depending on whether or not it is an array
		var oCopy = (Array.isArray( oObject ) ? [] : {});
		// Iterate over each property of the object
		for( var sProperty in oObject ){
			// If the property holds an object
			if( typeof( oObject[ sProperty ] ) === "object" ){
				// Recurse
				oCopy[ sProperty ] = deepCopy( oObject[ sProperty ] );
			// Otherwise
			} else {
				// Copy the value
				oCopy[ sProperty ] = oObject[ sProperty ];
			}
		}
 
		// Return the copy
		return oCopy;
	};
 
	/*********************************************************************/
	// 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;
	};
 
	/*********************************************************************/
	// 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;
	};
 
	/*********************************************************************/
	// DzObject : A function for getting the object for a node
	function getObjectForNode( oNode, bRoot )
	{
		// Get the node
		var oContextNode = bRoot ? getRootNode( oNode ) : oNode;
		// If we do not have a root node
		if( !oContextNode ){
			// We are done...
			return null;
		}
 
		// Get the object of the root node
		var oObject = oContextNode.getObject();
		// If we do not have an object
		if( !oObject ){
			// We are done...
			return null;
		}
 
		// Return the object
		return oObject;
	};
 
	/*********************************************************************/
	// DzShape : A function for getting the shape for a node
	function getShapeForNode( oNode, bRoot )
	{
		// Get the object of the node
		var oObject = getObjectForNode( oNode, bRoot );
		// If we do not have an object
		if( !oObject ){
			// We are done...
			return null;
		}
 
		// Get the shape of the root node
		var oShape = oObject.getCurrentShape();
		// If we do not have a shape
		if( !oShape ){
			// We are done...
			return null;
		}
 
		// Return the shape
		return oShape;
	};
 
	/*********************************************************************/
	// DzGeometry : A function for getting the geometry for the root of a node
	function getGeometryForNode( oNode, bRoot, bCached )
	{
		// Get the shape of the root node
		var oShape = getShapeForNode( oNode, bRoot );
		// If we do not have a shape
		if( !oShape ){
			// We are done...
			return null;
		}
 
		// If we are getting the cached geometry
		if( bCached ){
			// Update the working mesh
			//oShape.invalidateWorkingMesh();
 
			// Get the object of the root node
			var oObject = getObjectForNode( oNode, bRoot );
 
			// Return the geometry
			return oObject.getCachedGeom();
		}
 
		// Get the geometry of the root node
		var oGeometry = oShape.getGeometry();
		// If we do not have a geometry
		if( !oGeometry ){
			// We are done...
			return null;
		}
 
		// Return the geometry
		return oGeometry;
	};
 
	/*********************************************************************/
	// Array<DzNode> : A function for getting the followers of a node
	function getFollowNodes( oNode )
	{
		// If we do not have a node
		if( !oNode ){
			// Return an empty list
			return [];
		}
 
		// If we have a bone
		if( inheritsType( oNode, ["DzBone"] ) ){
			// Get the root node
			oNode = getRootNode( oNode );
		}
 
		// If we have a skeleton
		if( inheritsType( oNode, ["DzSkeleton"] ) ){
			// Get the number of skeletons following this one
			var nFollowers = oNode.getNumFollowSkeletons();
 
			// Pre-size the results array
			var aResult = new Array( nNodes );			
 
			// Iterate over the followers
			for( var i = 0; i < nFollowers; i += 1 ){
				// Get the 'current' follower
				oFollower = oNode.getFollowSkeleton( i );
 
				// Assign the node to the result
				aResult[i] = oFollower;
			}
 
			// Return the list
			return aResult;
		}
 
		// Return an empty list
		return [];
	};
 
	/*********************************************************************/
	// Array<DzNode> : A function for getting the nodes parented to a node
	function getNodeChildren( oNode, bRecurse )
	{
		// If we have a node
		if( oNode && inheritsType( oNode, ["DzNode"] ) ){
			// Return a list of the node children
			return oNode.getNodeChildren( bRecurse );
		}
 
		// Return an empty list
		return [];
	};
 
	/*********************************************************************/
	// Array<DzNode> : A function for extracting unique root nodes
	function getUniqueNodes( aNodes, bRoots, bFollowers, bParented, bRecursive )
	{
		// If we do not have an array
		if( typeof( aNodes ) != "object" || !Array.isArray( aNodes ) ){
			// We are done...
			return [];
		}
 
		// Get the number of nodes in the list
		var nNodes = aNodes.length;
 
		// Declare working variable
		var oNode;
 
		// If we are processing followers or parented nodes
		if( bFollowers || bParented ){
			// Declare working variable
			var aSubNodes;
 
			// Iterate over the nodes
			for( var i = 0; i < nNodes; i += 1 ){
				// Get the 'current' node
				oNode = aNodes[ i ];
 
				// If we are interested in followers
				if( bFollowers ){
					// Get the followers
					aSubNodes = getFollowNodes( oNode );
 
					// If we are recursing
					if( bRecursive ){
						// Get the unique nodes of the followers
						aSubNodes = getUniqueNodes( aSubNodes,
								false, bFollowers, bParented, bRecursive );
					}
 
					// Append the nodes
					aNodes = aNodes.concat( aSubNodes );
				}
 
				// If we are interested in parented nodes
				if( bParented ){
					// If we are interested in roots, recursively
					if( bRoots && bRecursive ){
						// Get the nodes in the hierarchy of the root
						aSubNodes = getNodeChildren( getRootNode( oNode ), true );
					// If we are not interested in roots
					} else {
						// Get the parented nodes
						aSubNodes = getNodeChildren( oNode, bRecursive );
					}
 
					// Append the nodes
					aNodes = aNodes.concat( aSubNodes );
				}
			}
		}
 
		// Update the number of nodes
		nNodes = aNodes.length;
 
		// Pre-size the results array
		var aResult = new Array( nNodes );		
 
		// Declare working variable
		var nNodeId;
 
		// Initialize
		var oNodeIds = {};
		var nIdx = 0;
 
		// Iterate over the list of nodes
		for( var i = 0; i < nNodes; i += 1 ){
			// Get the 'current' node
			oNode = aNodes[ i ];
 
			// If we do not have a node
			if( !oNode || !inheritsType( oNode, ["DzNode"] ) ){
				// Next!!
				continue;
			}
 
			// If we are interested in root nodes
			if( bRoots ){
				// Get the root for the node
				oNode = getRootNode( oNode );
			}
 
			// Get the element ID for the node
			nNodeId = oNode.elementID;
			// If we already have that ID
			if( oNodeIds[ nNodeId ] ){
				// Next!!
				continue;
			}
 
			// Record the node for the ID
			oNodeIds[ nNodeId ] = oNode;
 
			// Assign the node to the result
			aResult[ nIdx ] = oNode;
 
			// Increment our index
			nIdx += 1;
		}
 
		// Return the list, without any empty/invalid values
		return aResult.filter( Boolean );
	};
 
	/*********************************************************************/
	// 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.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 );
	};
 
	/*********************************************************************/
	// Boolean : A function for testing whether an element inherits a type
	function isSupportedModifierType( oElement )
	{
		// Define the list of modifier types that support the assetUri property
		var aTypes = [
			"DzDFormModifier",
			"DzLegacyBinding",
			"DzMeshSmoothModifier",
			"DzMorph",
			"DzPushModifier",
			"DzSceneAssetModifier",
				//"DzConditionalGraftModifier",
				//"DzLineTessellationModifier",
			"DzSkinBinding"
		];
 
		// Return the result
		return inheritsType( oElement, aTypes );
	};
 
	/*********************************************************************/
	// Boolean : A function for testing whether a texture inherits a type
	function isSupportedTextureType( oTexture )
	{
		// Define the list of texture types that support the assetUri property
		var aTypes = [ "DzImageTexture", "DzLayeredTexture" ];
 
		// Return the result
		return inheritsType( oTexture, aTypes );
	};
 
	/*********************************************************************/
	// void : A function for collecting product information from a local absolute path
	function captureAssetPathProductInfo( sAbsPath )
	{
		// If the path is empty
		if( sAbsPath.isEmpty() ){
			// We are done...
			return;
		}
 
		// Get the content manager
		var oContentMgr = App.getContentMgr();
		// If we do not have a content manager
		if( !oContentMgr ){
			// We are done...
			return;
		}
 
		// Get the relative path of the file
		var sRelPath = oContentMgr.getRelativePath( DzContentMgr.AllDirsAndCloud, sAbsPath );
		// If the path is not relative
		if( sRelPath == sAbsPath ){
			// We are done... (not mapped)
			return;
		}
 
		// Get the asset manager
		var oAssetMgr = App.getAssetMgr();
		// If we do not have a asset manager
		if( !oAssetMgr ){
			// We are done...
			return;
		}
 
		// Get the products that this relative path is in
		var aProducts = oAssetMgr.findProductsForFile( sRelPath );
 
		// Declare working variables
		var oProduct;
		var sGuid;
		var aPaths;
 
		// Iterate over the products
		for( var i = 0, nProducts = aProducts.length; i < nProducts; i += 1 ){
			// Get the 'current' product
			oProduct = aProducts[ i ];
			// Get the GUID
			sGuid = oProduct.guid;
 
			// Get the list of relative paths
			aPaths = s_oProductsMap[ sGuid ];
			// If the list does not exist
			if( !Array.isArray( aPaths ) ){
				// Initialize the list with the 'current' path
				aPaths = [ sRelPath ];
			// If the list does exist
			} else {
				// Assign the next item in the list
				aPaths[ aPaths.length ] = sRelPath;
			}
 
			// Update the products map
			s_oProductsMap[ sGuid ] = aPaths;
		}
	};
 
	/*********************************************************************/
	// void : A function for collecting product information for active
	// properties associated with an element
	function capturePropertiesProductInfo( oElement )
	{
		// If we do not have an element
		if( !oElement ){
			// We are done...
			return;
		}
 
		// Declare working variables
		var oProperty, oOwner, oUri;
		var sAbsPath, sProduct;
 
		// Get the properties available to the user by way of the selected node
		var aProperties = getElementProperties( oElement, true, true );
		// Iterate over all properties
		for( var i = 0; i < aProperties.length; i += 1 ){
			// Get the 'current' property
			oProperty = aProperties[ i ];
 
			// Get the owner of the property
			oOwner = oProperty.getOwner();	
 
			// If the owner is a material
			if( !inheritsType( oOwner, ["DzMaterial"] ) ){
				// If the property's value is the same as the definition
				if( oProperty.currentValueIsDefinitionValue() ){
					// Next!!
					continue;
				}
 
				// If the property is hidden
				if( oProperty.isHidden() || oProperty.isDynamicallyHidden() ){
					// Next!!
					continue;
				}
			}
 
			// If the owner inherits supported modifier types
			if( isSupportedModifierType( oOwner ) ){
				// Get the asset URI from the modifier
				oUri = oOwner.assetUri;
			// Otherwise
			} else {
				// Get the asset URI from the property
				oUri = oProperty.assetUri;
			}
 
			// Convert to a local file path
			sAbsPath = oUri.toLocalFilename();
			// If we do not have a local file
			if( sAbsPath.isEmpty() ){
				// Provide feedback
				debug( "No file found:", oProperty.assetId, ":", oProperty.getLabel() );
				// Next!!
				continue;
			}
 
			// Capture product info from the asset path
			captureAssetPathProductInfo( sAbsPath );
		}
	};
 
	/*********************************************************************/
	// void : A function for collecting product information from a UV set
	function captureUVSetProductInfo( oMap )
	{
		// If we do not have a map or it is not a UV Set
		if( !oMap || !inheritsType( oMap, ["DzUVSet"] ) ){
			// We are done...
			return;
		}
 
		// Get the asset URI from the map
		var oUri = oMap.assetUri;
 
		// Convert to a local file path
		var sAbsPath = oUri.toLocalFilename();
		// If we do not have a local file
		if( sAbsPath.isEmpty() ){
			// Provide feedback
			debug( "No file found:", oMap.assetId, ":", oMap.getLabel() );
			// We are done...
			return;
		}
 
		// Capture product info from the asset path
		captureAssetPathProductInfo( sAbsPath );
	};
 
	/*********************************************************************/
	// void : A function for collecting product information from a geometry
	function captureFacetMeshProductInfo( oGeometry )
	{
		// If we do not have a geometry or it is not a facet mesh
		if( !oGeometry || !inheritsType( oGeometry, ["DzFacetMesh"] ) ){
			// We are done...
			return;
		}
 
		// Get the asset URI from the geometry
		var oUri = oGeometry.assetUri;
 
		// Convert to a local file path
		var sAbsPath = oUri.toLocalFilename();
		// If we do not have a local file
		if( sAbsPath.isEmpty() ){
			// Provide feedback
			debug( "No file found:", oGeometry.assetId, ":", oGeometry.getName() );
			// We are done...
			return;
		}
 
		// Capture product info from the asset path
		captureAssetPathProductInfo( sAbsPath );
	};
 
	/*********************************************************************/
	// void : A function for collecting product information from a texture
	function captureTextureProductInfo( oTexture )
	{
		// If we do not have a texture
		if( !oTexture ){
			// We are done...
			return;
		}
 
		// Get the path of the file
		var sAbsPath = oTexture.getFilename();
 
		// If the texture is not a type we can handle
		if( !isSupportedTextureType( oTexture ) ){
			// If we do not have a local file
			if( sAbsPath.isEmpty() ){
				// Provide feedback
				debug( "No file found:", oTexture.assetId, ":", sAbsPath );
				// We are done...
				return;
			}
 
			// Capture product info from the asset path
			captureAssetPathProductInfo( sAbsPath );
 
			// We are done...
			return;
		}
 
		// If we do not have a local file
		if( sAbsPath.isEmpty() ){
			debug( "No file found:", oTexture.assetId, ":", sAbsPath );
			// We are done...
			return;
		}
 
		// Capture product info from the asset path
		captureAssetPathProductInfo( sAbsPath );
 
		// Get the asset URI from the geometry
		var oUri = oTexture.assetUri;
 
		// Convert to a local file path
		sAbsPath = oUri.toLocalFilename();
		// If we do not have a local file
		if( sAbsPath.isEmpty() ){
			debug( "No file found:", oTexture.assetId, ":", sAbsPath );
			// We are done...
			return;
		}
 
		// Capture product info from the asset path
		captureAssetPathProductInfo( sAbsPath );
	};
 
	/*********************************************************************/
	// void : A function for collecting product information from materials
	function captureMaterialsProductInfo( oShape )
	{
		// If we do not have a shape
		if( !oShape ){
			// We are done...
			return;
		}
 
		// Declare working variables
		var oMaterial, oUri;
		var sAbsPath;
		var aMaps;
 
		// Get the materials on the current shape
		var aMaterials = oShape.getAllMaterials();
		// Iterate over the materials
		for( var i = 0, nMaterials = aMaterials.length; i < nMaterials; i += 1 ){
			// Get the 'current' material
			oMaterial = aMaterials[ i ];
 
			// Capture product info from the active UV set
			captureUVSetProductInfo( oMaterial.getActiveUVSet( oShape ) );
 
			// Get all the maps used by the material
			aMaps = oMaterial.getAllMaps();
			// Iterate over the maps
			for( var j = 0, nMaps = aMaps.length; j < nMaps; j += 1 ){
				// Capture product info from the map
				captureTextureProductInfo( aMaps[ j ] );
			}
 
			// Get the asset URI from the material
			oUri = oMaterial.assetUri;
 
			// Convert to a local file path
			sAbsPath = oUri.toLocalFilename();
			// If we do not have a local file
			if( sAbsPath.isEmpty() ){
				// Provide feedback
				debug( "No file found:", oMaterial.assetId, ":", oMaterial.getLabel() );
				// Next!!
				continue;
			}
 
			// Capture product info from the asset path
			captureAssetPathProductInfo( sAbsPath );
 
			// Capture product info from properties on the material
			//capturePropertiesProductInfo( oMaterial );
		}
 
		// Declare working variables
		var oProvider;
 
		// Get the list of simulation settings provider names
		var aSimProviderNames = oShape.getSimulationProviderNames();
		// Iterate over the simulation provider names
		for( var i = 0, nProviders = aSimProviderNames.length; i < nProviders; i += 1 ){
			// Get the 'current' simulation settings provider
			oProvider = oShape.findSimulationSettingsProvider( aSimProviderNames[ i ] );
 
			// Get the asset URI from the material
			oUri = oProvider.assetUri;
 
			// Convert to a local file path
			sAbsPath = oUri.toLocalFilename();
			// If we do not have a local file
			if( sAbsPath.isEmpty() ){
				// Provide feedback
				debug( "No file found:", oProvider.assetId, ":", oProvider.getName() );
				// Next!!
				continue;
			}
 
			// Capture product info from the asset path
			captureAssetPathProductInfo( sAbsPath );
		}
	};
 
	/*********************************************************************/
	// void : A function for collecting product information from modifiers
	function captureModifiersProductInfo( oObject )
	{
		// If we do not have a object
		if( !oObject ){
			// We are done...
			return;
		}
 
		// Declare working variables
		var oModifier, oUri;
		var sAbsPath;
		var bSupported;
 
		// Iterate over the modifiers
		for( var i = 0, nModifiers = oObject.getNumModifiers(); i < nModifiers; i += 1 ){
			// Get the 'current' modifier
			oModifier = oObject.getModifier( i );
 
			// Get whether the modifier type is supported
			bSupported = isSupportedModifierType( oModifier );
 
			// If it is not a type that supports the assetUri property
			if( !bSupported ){
				// Next!!
				continue;
			}
 
			// Get the asset URI from the modifier
			oUri = oModifier.assetUri;
 
			// Convert to a local file path
			sAbsPath = oUri.toLocalFilename();
			// If we do not have a local file
			if( sAbsPath.isEmpty() ){
				// Provide feedback
				debug( "No file found:", oModifier.assetId, ":", oModifier.getName() );
				// Next!!
				continue;
			}
 
			// Capture product info from the asset path
			captureAssetPathProductInfo( sAbsPath );
 
			// Capture product info from properties on the modifier
			//capturePropertiesProductInfo( oModifier );
		}
	};
 
	/*********************************************************************/
	// void : A function for collecting product information from a node
	function captureNodeProductInfo( oNode )
	{
		// Get the asset manager
		var oAssetMgr = App.getAssetMgr();
		// If we do not have an asset manager
		if( !oAssetMgr ){
			// We are done...
			return;
		}
 
		// Get the asset URI path for the node
		var sUri = oAssetMgr.getAssetUriForNode( oNode );
		// Create a URI from the path
		var oUri = new DzUri( sUri );
		// Convert the URI to an absolute local file path
		var sAbsPath = oUri.toLocalFilename();
		// If the path is empty
		if( sAbsPath.isEmpty() ){
			// We are done...
			return;
		}
 
		// Capture product info from the asset path
		captureAssetPathProductInfo( sAbsPath );
 
		// Capture product info from properties on the node
		capturePropertiesProductInfo( oNode );
	};
 
	/*********************************************************************/
	// Object : A function for getting products information
	function getProductsInfo( bUnified )
	{
		// Initialize
		var oProducts = {};
 
		// Get the asset manager
		var oAssetMgr = App.getAssetMgr();
		// If we do not have an asset manager
		if( !oAssetMgr ){
			// We are done...
			return oProducts;
		}
 
		// Declare working variables
		var oProduct, oProductInfo;
		var aGuids, aPaths;
		var sGuid;
 
		// Get the list of product GUIDs
		var aGuids = getKeys( s_oProductsMap );
		// Iterate over the list of GUIDs
		for( var i = 0, nGuids = aGuids.length; i < nGuids; i += 1 ){
			// Get the 'current' GUID
			sGuid = aGuids[ i ];
 
			// Get the product for the GUID
			oProduct = oAssetMgr.findProductByGuid( sGuid );
 
			// Initialize; capture the title
			oProductInfo = { "title" : oProduct.title };
 
			//If we are unifying the results
			if( bUnified ){
				// Capture the product GUID
				oProductInfo[ "guid" ] = sGuid;
			}
 
			// Get the list of paths
			aPaths = s_oProductsMap[ sGuid ];
			// Sort and remove duplicates
			aPaths = aPaths.sort().filter(
				function( sValue, nIdx, aList ){
					return !nIdx || sValue != aList[ nIdx - 1 ];
				});
 
			// Capture the paths
			oProductInfo[ "files" ] = aPaths;
 
			// Capture the product info
			oProducts[ sGuid ] = oProductInfo;
		}
 
		// Return the result
		return oProducts;
	};
 
	/*********************************************************************/
	// Object : A function for getting products information for a node
	function getNodeProductsMap( oNode, bUnified )
	{
		// Declare working variable
		var oProductsMap;
 
		// If we are unifying the results and there is something to copy
		if( bUnified && getKeys( s_oProductsMap ).length > 0 ){
			// Copy the current products map
			oProductsMap = deepCopy( s_oProductsMap );
		}
 
		// Initialize for this node
		s_oProductsMap = {};
 
		// Capture product information
		captureNodeProductInfo( oNode );
 
		// We do not need to do this because it is already being handled
		// by the properties being handled by the node
		/*
		// Get the object 
		var oObject = getObjectForNode( oNode, false );
 
		// If we have an object
		if( oObject ){
			// Capture product information
			captureModifiersProductInfo( oObject );
		}
		*/
 
		// Get the current shape 
		var oShape = getShapeForNode( oNode, false );
		// If we have a shape
		if( oShape ){
			// Capture product information
			captureMaterialsProductInfo( oShape );
		}
 
		// Get the geometry 
		var oGeometry = getGeometryForNode( oNode, false, false );
		// If we have a geometry
		if( oGeometry ){
			// Capture product information
			captureFacetMeshProductInfo( oGeometry );
		}
 
		// Get the product GUIDs
		var aGuids = getKeys( s_oProductsMap );
 
		// If we have a stored products map
		if( oProductsMap ){
			// Declare working variables
			var sGuid;
			var aMapFiles, aNodeFiles;
			var bHasMapFiles, bHasNodeFiles;
 
			// Iterate over the product GUIDs
			for( var i = 0, nGuids = aGuids.length; i < nGuids; i += 1 ){
				// Get the 'current' GUID
				sGuid = aGuids[ i ];
 
				// Get the stored map file list
				aMapFiles = oProductsMap[ sGuid ];
				bHasMapFiles = Array.isArray( aMapFiles );
 
				// Get the node file list
				aNodeFiles = s_oProductsMap[ sGuid ];
				bHasNodeFiles = Array.isArray( aNodeFiles );
 
				// If we have lists to merge
				if( bHasMapFiles && bHasNodeFiles ){
					// Append this node files to the map
					aMapFiles = aMapFiles.concat( aNodeFiles );
					// Update the map
					oProductsMap[ sGuid ] = aMapFiles;
				// If we only have node files
				} else if( bHasNodeFiles ){
					// Update the map
					oProductsMap[ sGuid ] = aNodeFiles;
				}
			}
 
			// Update the product map
			s_oProductsMap = deepCopy( oProductsMap );
		}
 
		// If we are unifying the results
		if( bUnified ){
			// Return the product GUIDs
			return aGuids;
		}
 
		// Initialize
		var oNodeInfo = {};
 
		// Capture the node label
		oNodeInfo[ "node" ] = oNode.getLabel();
 
		// Capture the products used by the node
		oNodeInfo[ "products" ] = getProductsInfo( bUnified );
 
		// Return the node products information
		return oNodeInfo;
	};
 
	/*********************************************************************/
	// Object : A function for getting unified products information
	function getUnifiedProductsMap( oNodeGuidMap )
	{
		// Get the products information
		var oProductsMap = getProductsInfo( true );
 
		// Declare working variables
		var sNodeID, sNodeLabel, sGuid;
		var oNodeInfo, oInfo;
		var aGuids, aNodes;
 
		// Get the node IDs
		var aNodeIDs = getKeys( oNodeGuidMap );
		// Iterate over the node IDs
		for( var i = 0, nNodeIDs = aNodeIDs.length; i < nNodeIDs; i += 1 ){
			// Get the 'current' node ID
			sNodeID = aNodeIDs[ i ];
			// Get the node info
			oNodeInfo = oNodeGuidMap[ sNodeID ];
			// Get the node label
			sNodeLabel = oNodeInfo[ "label" ];
			// Get the product GUIDs
			aGuids = oNodeInfo[ "guids" ];
			// Iterate over the GUIDs
			for( var j = 0, nGuids = aGuids.length; j < nGuids; j += 1 ){
				// Get the 'current' GUID
				sGuid = aGuids[ j ];
				// Get the product info
				oInfo = oProductsMap[ sGuid ];
				// Get the list of nodes
				aNodes = oInfo[ "nodes" ];
				// If the list does not exist
				if( !Array.isArray( aNodes ) ){
					// Initialize the list
					aNodes = [ sNodeLabel ];
				// If the list does exist
				} else {
					// Assign the next item in the list
					aNodes[ aNodes.length ] = sNodeLabel;
				}
 
				// Update the list of nodes
				oInfo[ "nodes" ] = aNodes;
 
				// Update the product info
				oProductsMap[ sGuid ] = oInfo;
			}
		}
 
		// Return the unified 
		return oProductsMap;
	};
 
	/*********************************************************************/
	// Define common strings
	var sTitle = text( "Selection Error" );
	var sMessage = text( "A node in the scene must be selected to continue." );
	var sButton = text( "&OK" );
 
	// Declare working variables
	var oNode;
	var aNodes;
 
	// Define a 'static' variable to collect product data
	var s_oProductsMap = {};
 
	// Define whether or not we want to unify (combine)
	// the node product result(s) in a single product map
	var bUnifyProducts = true;
 
	// Define whether or not we want to limit which nodes we process
	var bSelectedNodes = false;
 
	// If we are processing the selected nodes only
	if( bSelectedNodes ){
		// Get the primary selection
		oNode = Scene.getPrimarySelection();
		// If nothing is selected
		if( !oNode ){
			// Inform the user
			MessageBox.information( sMessage, sTitle, sButton );
 
			// We are done..
			return;
		}
 
		// Define how to handle the selection
		var bRoots = true;
		var bFollowers = true;
		var bParented = true;
		var bRecursive = true;
 
		// Get the list of unique selected nodes
		aNodes = getUniqueNodes( Scene.getSelectedNodeList(),
				bRoots, bFollowers, bParented, bRecursive );
	// If we are processing all nodes in the scene
	} else {
		// Get the list of unique root nodes
		aNodes = getUniqueNodes( Scene.getNodeList(),
				true, false, false, false );
	}
 
	// Let the user know we are busy
	setBusyCursor();
 
	// Declare working variable
	var oNodeInfo;
 
	// Initialize
	var oNodeGuidMap = {};
 
	// Iterate over the root nodes
	for( var i = 0, nNodes = aNodes.length; i < nNodes; i += 1 ){
		// Get the 'current' node
		oNode = aNodes[ i ];
 
		// Get the product information for the node
		oNodeInfo = getNodeProductsMap( oNode, bUnifyProducts );
 
		// If we are not unifying results
		if( !bUnifyProducts ){
			// Provide feedback
			print( getStringified( oNodeInfo ) );
 
			// Next!!
			continue;
		}
 
		// Get the list of product GUIDs
		oNodeGuidMap[ oNode.elementID ] = {
				"label" : oNode.getLabel(),
				"guids" : oNodeInfo
			};
	}
 
	// If we are unifying results
	if( bUnifyProducts ){
		// Get the unified products map
		var oUnifiedMap = getUnifiedProductsMap( oNodeGuidMap );
 
		// Provide feedback
		print( getStringified( getValues( oUnifiedMap ) ) );
	}
 
	// Let the user know we are done
	clearBusyCursor();
 
// Finalize the function and invoke
})();