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.
// 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 })();