User Tools

Site Tools


Generate Polyline Dynamic Surface AddOn

Summary

Below is an example that demonstrates the use of the geometry pipeline to create polylines between pairs of selected vertices in a mesh. Each of the generated polylines is collected into a single node that is parented to the source node. Add a dForce modifier to the result to use it as a Dynamic Surface AddOn that adds additional support/structure to a dynamic surface.

Usage

  1. Activate the “Geometry Editor” tool
  2. Set “Selection Type” to “Vertex Selection”
  3. Select 2 vertices in the host mesh that you want to create a polyline between
  4. Execute the script
    • Create a Custom Action and bind a shortcut or put it in a toolbar/menu
  5. Repeat the previous 2 steps as needed
  6. “Add dForce Modifier : Dynamic Surface Add-On” to the created “<host_node_label> AddOn” node
    • AddOn node is created by the script upon first execution
    • AddOn node will be parented to the host node in the Scene hierarchy

API Areas of Interest

Example

Generate_Polyline_Dynamic_Surface_AddOn.dsa
// DAZ Studio version 4.11.0.36 filetype DAZ Script
 
// 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(" ") );
	};
 
	/*********************************************************************/
	// 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;
	};
 
	/*********************************************************************/
	// DzFacetMesh : A function for getting the facet mesh for a node
	function getFacetMeshForNode( oNode, bRoot, bCached )
	{
		// Get the geometry of the node
		var oGeometry = getGeometryForNode( oNode, bRoot, bCached );
		// If we do not have a facet mesh
		if( !inheritsType( oGeometry, ["DzFacetMesh"] ) ){
			// We are done...
			return null;
		}
 
		// Return the geometry
		return oGeometry;
	};
 
	/*********************************************************************/
	// Get the selected nodes
	var aNodes = Scene.getSelectedNodeList();
 
	// Declare working variables
	var oSrcNode, oTgtNode;
 
	// If we have 2 or more nodes selected
	if( aNodes.length > 1 ){
		// The source node is the one selected later; primary selection
		oSrcNode = aNodes[ 1 ];
		// The target node is the ones selected first
		oTgtNode = aNodes[ 0 ];
	// If we have 1 node selected
	} else if( aNodes.length > 0 ){
		// The source node is the primary selection
		oSrcNode = aNodes[ 0 ];
	}
 
	// If nothing is selected
	if( !oSrcNode ){
		// We are done...
		return;
	}
 
	// Get the base mesh of the root node
	var oSrcBaseMesh = getFacetMeshForNode( oSrcNode, true, false );
	// If we do not have a base mesh
	if( !oSrcBaseMesh ){
		// We are done...
		return;
	}
 
	// Define the target node name
	var sTgtNodeName = String("%1_addon").arg( oSrcNode.objectName );
 
	// Initialize
	var bTgtNodeCreated = false;
 
	// If we do not have a target node
	if( !oTgtNode ){
		// Find the target node in the hierarchy of the source node
		oTgtNode = oSrcNode.findNodeChild( sTgtNodeName, true );
	}
 
	// If we still do not have a target node,
	// or the node we have it is not named as we expect
	if( !oTgtNode || oTgtNode.objectName != sTgtNodeName ){
		// Create a new target node
		oTgtNode = new DzNode();
 
		// Update variable state
		bTgtNodeCreated = true;
 
		// Set the target node name
		oTgtNode.setName( sTgtNodeName );
 
		// Get the label of the source node
		var sLabel = oSrcNode.getLabel();
		// Strip the number from the label
		sLabel = Scene.stripLabelNumber( sLabel );
		// Get a unique label
		sLabel = Scene.getUniqueTopLevelLabel( String("%1 AddOn").arg( sLabel ) );
		// Set the label of the shell node
		oTgtNode.setLabel( sLabel );
	}
 
	// Let the user know we are busy
	setBusyCursor();
 
	// Get the source object
	var oSrcObject = getObjectForNode( oSrcNode, true );
 
	// Get the target object
	var oTgtObject = !bTgtNodeCreated ? getObjectForNode( oTgtNode, true ) : undefined;
 
	// Initialize
	var bTgtObjectCreated = false;
 
	// If we do not have a target object
	if( !oTgtObject ){
		// Create a new target object
		oTgtObject = new DzObject();
 
		// Update variable state
		bTgtObjectCreated = true;
 
		// Set the target object name
		oTgtObject.name = oSrcObject.name + "_polylines";
	}
 
	// Get the source shape
	var oSrcShape = getShapeForNode( oSrcNode, true );
 
	// Get the target shape
	var oTgtShape = !bTgtObjectCreated ? getShapeForNode( oTgtNode, true ) : undefined;
 
	// Initialize
	var bTgtShapeCreated = false;
 
	// If we do not have a target shape
	if( !oTgtShape ){
		// Create a new facet shape for the target
		oTgtShape = new DzFacetShape();
 
		// Update variable state
		bTgtShapeCreated = true;
 
		// Set the target shape name and label
		oTgtShape.name = oSrcShape.name;
		oTgtShape.setLabel( oSrcShape.getLabel() );
	}
 
	// Get the target facet mesh
	var oTgtMesh = !bTgtShapeCreated ? getFacetMeshForNode( oTgtNode, true, false ) : undefined;
 
	// Initialize
	var bTgtMeshCreated = false;
 
	// If we do not have a target facet mesh
	if( !oTgtMesh ){
		// Create a new target facet mesh
		oTgtMesh = new DzFacetMesh();
 
		// Update variable state
		bTgtMeshCreated = true;
	}
 
	// Define the material name
	var sMaterialName = "Default";
 
	// Find the target material
	var oTgtMaterial = oTgtShape.findMaterial( sMaterialName );
 
	// Initialize
	var bTgtMaterialCreated = false;
 
	// If we do not have a target material
	if( !oTgtMaterial ){
		// Create a new target default material
		oTgtMaterial = new DzDefaultMaterial();
 
		// Update variable state
		bTgtMaterialCreated = true;
 
		// Set the material name and label;
		// use a name that is consistent with the create primitive action
		oTgtMaterial.name = sMaterialName;
		oTgtMaterial.setLabel( oTgtMaterial.name );
 
		// Set the color of the material so that we can
		// distinguish it as a Dynamic Surface AddOn
		oTgtMaterial.setDiffuseColor( new Color( 0, 255, 255 ) );
 
		// Add the material to the shape
		oTgtShape.addMaterial( oTgtMaterial );
	}
 
	// Initialize a local vertex index array
	var aSrcVertices = oSrcBaseMesh.getSelectedVertices();
 
	// Get the number of edges
	var nSrcVertices = aSrcVertices.length;
 
	// If we have more than one vertex selected
	if( nSrcVertices > 1 ){
		// Begin editing the target mesh
		oTgtMesh.beginEdit();
 
		// Activate the material; all new geometry will be added to this
		oTgtMesh.activateMaterial( sMaterialName );
 
		// If we created the target mesh
		if( bTgtMeshCreated ){
			// Pre-size the vertex array
			oTgtMesh.preSizeVertexArray( 2 );
 
			// Pre-size the polylines array
			oTgtMesh.preSizePolylines( 1, 0 );
		}
 
		// Declare working variables
		var aTgtVertexIndices, aTgtUVIndices;
 
		// Get the first and second vertex indices;
		// the selected vertex list is not sorted according
		// to selection, it is sorted according to index;
		// using more than 2 vertex indices is unreliable
		var nSrcVertexIdx1 = aSrcVertices[ 0 ];
		var nSrcVertexIdx2 = aSrcVertices[ 1 ];
 
		// Get the number of vertices in the target mesh
		var nTgtVertices = oTgtMesh.getNumVertices();
 
		// Get the positions of the source vertices
		var vecVertex1 = oSrcBaseMesh.getVertex( nSrcVertexIdx1 );
		var vecVertex2 = oSrcBaseMesh.getVertex( nSrcVertexIdx2 );
 
		// Provide feedback
		debug( String("\tSource Vertex1 #%1").arg( nSrcVertexIdx1 ),
				vecVertex1.x, vecVertex1.y, vecVertex1.z );
		debug( String("\tSource Vertex2 #%1").arg( nSrcVertexIdx2 ),
				vecVertex2.x, vecVertex2.y, vecVertex2.z );
 
		// Initialize
		var bCreatePolyline = true;
 
		// Declare working variables
		var bMatch1, bMatch2;
		var aLineVertexIndices;
		var nLineVertex1Idx, nLineVertex2Idx;
		var vecLineVertex1, vecLineVertex2;
 
		// Get the number of polylines in the target mesh
		var nPolylines = oTgtMesh.getNumPolylines();
		// Iterate over the polylines
		for( var i = 0; i < nPolylines; i += 1 ){
			// Get the list of vertex indices for the 'current' polyline
			aLineVertexIndices = oTgtMesh.getPolylineVertexIndices( i );
			// If there are more than 2 indices;
			// it cannot be a polyline we created
			if( aLineVertexIndices.length != 2 ){
				// Next!!
				continue;
			}
 
			// Get the polyline vertex indices
			nLineVertex1Idx = aLineVertexIndices[ 0 ];
			nLineVertex2Idx = aLineVertexIndices[ 1 ];
 
			// Get the positions of the target mesh polyline vertices
			vecLineVertex1 = oTgtMesh.getVertex( nLineVertex1Idx );
			vecLineVertex2 = oTgtMesh.getVertex( nLineVertex2Idx );
 
			// Provide feedback
			debug( String("\tLine #%1 Vertex1 #%2").arg( i ).arg( nLineVertex1Idx ),
				vecLineVertex1.x, vecLineVertex1.y, vecLineVertex1.z );
			debug( String("\tLine #%1 Vertex2 #%2").arg( i ).arg( nLineVertex2Idx ),
				vecLineVertex2.x, vecLineVertex2.y, vecLineVertex2.z );
 
			// Initialize
			bMatch1 = false;
			bMatch2 = false;
 
			// If the vertex positions match
			if( vecVertex1.equals( vecLineVertex1 ) ){
				// Update variable state
				bMatch1 = true;
			// If the vertex positions match
			} else if ( vecVertex1.equals( vecLineVertex2 ) ){
				// Update variable state
				bMatch2 = true;
			}
 
			// If the other vertex position matches
			if( (bMatch1 && vecVertex2.equals( vecLineVertex2 )) ||
			(bMatch2 && vecVertex2.equals( vecLineVertex1 )) ){
				// Update variable state
				bCreatePolyline = false;
				// We have found a match
				break;
			}
		}
 
		// If we are creating the polyline
		if( bCreatePolyline ){
			// Add the first vertex to the target mesh
			var nTgtVertex1Idx = oTgtMesh.addVertex( vecVertex1 );
 
			// Add the second vertex to the target mesh
			var nTgtVertex2Idx = oTgtMesh.addVertex( vecVertex2 );
 
			// Get the positions of the target vertices
			vecVertex1 = oTgtMesh.getVertex( nTgtVertex1Idx );
			vecVertex2 = oTgtMesh.getVertex( nTgtVertex2Idx );
 
			// Provide feedback
			debug( String("\tTarget Vertex1 #%1").arg( nTgtVertex1Idx ),
					vecVertex1.x, vecVertex1.y, vecVertex1.z );
			debug( String("\tTarget Vertex2 #%1").arg( nTgtVertex2Idx ),
					vecVertex2.x, vecVertex2.y, vecVertex2.z );
 
			// Assign the vertex indices
			aTgtVertexIndices = [ nTgtVertex1Idx, nTgtVertex2Idx ];
 
			// Assign the UV indices;
			// we do not care about UVs for the add-on,
			// but because we need default UVs in order to
			// be loaded from a saved asset, we use the first
			// index of the default UV map created when we
			// created the target facet mesh
			aTgtUVIndices = [ 0, 0 ];
 
			// Add the polyline to the target mesh
			oTgtMesh.addPolyline( aTgtVertexIndices, aTgtUVIndices );
		}
 
		// Cause the UV set to be recreated from the
		// UV map when mesh editing is finished
		oTgtMesh.removeAllUVSets();
 
		// Finish editing the target mesh
		oTgtMesh.finishEdit();
	}
 
	// If we created the target mesh
	if( bTgtMeshCreated ){
		// Set the target mesh for the target shape
		oTgtShape.setFacetMesh( oTgtMesh );
	}
 
	// If we created the target shape
	if( bTgtShapeCreated ){
		// Add the target shape to the target object
		oTgtObject.addShape( oTgtShape );
	}
 
	// If we created the target object
	if( bTgtObjectCreated ){
		// Add the target object to the target node
		oTgtNode.setObject( oTgtObject );
	}
 
	// If the target node was not already in the scene
	if( bTgtNodeCreated ){
		// Add the target node to the scene
		Scene.addNode( oTgtNode );
	}
 
	// Get the target node parent
	var oTgtParent = oTgtNode.getNodeParent();
	// If we do not have a parent or the parent is not the source
	if( !oTgtParent || oTgtParent.elementID != oSrcNode.elementID ){
		// Parent the target node (in place) to the source node
		oSrcNode.addNodeChild( oTgtNode, true );
	}
 
	// If we created the target node or there was only one node selected
	if( bTgtNodeCreated || aNodes.length < 2 ){
		// Clear the scene selection
		Scene.selectAllNodes( false );
 
		// Select the target node
		oTgtNode.select( true );
		// Select the source node
		oSrcNode.select( true );
	}
 
	// Let the user know we are done
	clearBusyCursor();
 
// Finalize the function and invoke
})();