LScript (Modeler) creates cables between selected points. You can select more than two points to make it create multiple cables.
All cables are UV Mapped and fully setup for animation in LightWave by just cutting and pasting the two point polys (Surface:DYNAMIC) to one layer and the cables (surface:Cable) in the next layer parenting cables to the Dynamic layer. It was designed to be converted to subdivision surfaces for rendering.
Changes
- Added control reset
- Realtime Preview
Compatible with Newtek LightWave 9.6 and above.
// LScript Modeler - www.StephenCulley.co.uk // // web address: http://www.stephenculley.co.uk // email address: email@stephenculley.co.uk /* LScript Modeler - Cable Modeler_Cable.ls */ @version 2.2 @warnings @script modeler @name *Cable // Title sTitle = "*Cable"; // Version sVersion = "v2.0"; bDone = false; // Done bChange = false; // Change bPreview; // Preview bPreview; nRadius; nRadiusRandom; iSegments; bCapEnd; iSeed; vnGravity; nDroop; nDroopRandom; nTurbulance; nTurbulanceScale; nTurbulanceRandom; nNoise; iUV; ctrl_c0,ctrl_c1,ctrl_c2,ctrl_c3,ctrl_c4,ctrl_c5,ctrl_c6,ctrl_c7,ctrl_c8,ctrl_c9,ctrl_c10,ctrl_c11,ctrl_c12; ctrl_res0; ctrl_prev0; main { // Recall bPreview = recall("bPreview",true); nRadius = recall("nRadius",0.05); nRadiusRandom = recall("nRadiusRandom",0.0); iSegments = recall("iSegments",20); bCapEnd = recall("bCapEnd",false); iSeed = recall("iSeed",1); vnGravity = recall("vnGravity",<0.0,-9.8,0.0>); nDroop = recall("nDroop",0.25); nDroopRandom = recall("nDropRandom",0.0); nTurbulance = recall("nTurbulance",0.0); nTurbulanceScale = recall("nTurbulanceScale",1.0); nTurbulanceRandom = recall("nTurbulanceRandom",0.1); nNoise = recall("nNoise",0.0); iUV = recall("iUV",3); reqbegin(sTitle + " " + sVersion); // Reset / Preview ctrl_res0 = ctlbutton("Reset",50,"button_reset"); // Button Reset ctrl_prev0 = ctlcheckbox("Preview",bPreview); // Preview ctlsep(); // Control ctrl_c0 = ctldistance("Radius",nRadius); ctrl_c1 = ctldistance("Radius Random",nRadiusRandom); ctrl_c2 = ctlinteger("Segments",iSegments); ctrl_c3 = ctlcheckbox("Cap Ends (cross-section)",bCapEnd); ctlsep(); ctrl_c4 = ctlinteger("Seed",iSeed); ctlsep(); ctrl_c5 = ctlvector("Gravity",vnGravity); ctrl_c6 = ctlpercent("Droop",nDroop); ctrl_c7 = ctlpercent("Droop Random",nDroopRandom); ctlsep(); ctrl_c8 = ctldistance("Turbulance",nTurbulance); ctrl_c9 = ctlnumber("Turbulance Scale",nTurbulanceScale); ctrl_c10 = ctlnumber("Turbulance Random",nTurbulanceRandom); ctlsep(); ctrl_c11 = ctldistance("Noise Amount",nNoise); ctlsep(); ctrl_c12 = ctlchoice("Make UVs",iUV,@"Segment","Object","Ratio (1:1)"@,false); // Developer ctlsep(); ctrl_dev0 = ctltext("","developer: Stephen Culley","http://www.stephenculley.co.uk"); process(); // Process // Refresh ctlrefresh(ctrl_prev0,"refresh"); ctlrefresh(ctrl_c0,"refresh"); ctlrefresh(ctrl_c1,"refresh"); ctlrefresh(ctrl_c2,"refresh"); ctlrefresh(ctrl_c3,"refresh"); ctlrefresh(ctrl_c4,"refresh"); ctlrefresh(ctrl_c5,"refresh"); ctlrefresh(ctrl_c6,"refresh"); ctlrefresh(ctrl_c7,"refresh"); ctlrefresh(ctrl_c8,"refresh"); ctlrefresh(ctrl_c9,"refresh"); ctlrefresh(ctrl_c10,"refresh"); ctlrefresh(ctrl_c11,"refresh"); ctlrefresh(ctrl_c12,"refresh"); if (!reqpost()) { // Cancel if(bDone){undo();} // Undo return; } else { // Ok if(!bDone || bChange) // Process { values(); // Values process(); // Process } // Store store("bPreview",bPreview); store("nRadius",nRadius); store("nRadiusRandom",nRadiusRandom); store("iSegments",iSegments); store("bCapEnd",bCapEnd); store("iSeed",iSeed); store("vnGravity",vnGravity); store("nDroop",nDroop); store("nDropRandom",nDroopRandom); store("nTurbulance",nTurbulance); store("nTurbulanceScale",nTurbulanceScale); store("nTurbulanceRandom",nTurbulanceRandom); store("nNoise",nNoise); store("iUV",iUV); } reqend(); } refresh:value { bChange = true; // Change values(); // Values if(bPreview){process();} // Process } values { bPreview = getvalue(ctrl_prev0); // Preview nRadius = getvalue(ctrl_c0); // n Radius nRadiusRandom = getvalue(ctrl_c1); // n Radius Random iSegments = getvalue(ctrl_c2); // i Segments bCapEnd = getvalue(ctrl_c3); // b Cap End iSeed = getvalue(ctrl_c4); // i Seed vnGravity = normalize3D(getvalue(ctrl_c5)); // vn Gravity nDroop = getvalue(ctrl_c6); // n Droop nDroopRandom = getvalue(ctrl_c7); // n Droop Random nTurbulance = getvalue(ctrl_c8); // n Turbulance if(getvalue(ctrl_c9) <> 0.0){nTurbulanceScale = 1 / getvalue(ctrl_c9);}else{nTurbulanceScale = 0.0;} // n Turbulance Scale nTurbulanceRandom = getvalue(ctrl_c10); // n Turbulance Random nNoise = getvalue(ctrl_c11); // n Noise iUV = getvalue(ctrl_c12); // i UV } button_reset { setvalue(ctrl_c0,0.05); // Radius setvalue(ctrl_c1,0.0); // Radius Random setvalue(ctrl_c2,20); // Segments setvalue(ctrl_c3,false); // Cap End setvalue(ctrl_c4,1); // Seed setvalue(ctrl_c5,<0.0,-9.8,0.0>); // Gravity setvalue(ctrl_c6,0.25); // Droop setvalue(ctrl_c7,0.0); // Droop Random setvalue(ctrl_c8,0.0); // Turbulance setvalue(ctrl_c9,1.0); // Turbulance Scale setvalue(ctrl_c10,0.1); // Turbulance Random setvalue(ctrl_c11,0.0); // Noise setvalue(ctrl_c12,3); // UV } process { // Undo if(bDone){undo();} undogroupbegin(); // Process // Selection - Point (GLOBAL) selmode(GLOBAL); iPointCountBefore = pointcount(); // Selection - Point (DIRECT) selmode(DIRECT); iPointCount = pointcount(); if(iPointCount <= 1) error("None or not enough points selected."); if((iPointCount & 1) != 0) iPointCount--; // Check if odd editbegin(); mapFIXED = VMap(VMSELECT,"FIXED",1); // Selection Map mapUV = VMap(VMTEXTURE,"UV_Cable",2); // UV Map editend(); noiseseed(iSeed); // Noise Seed moninit((iPointCount / 2),"Processing..."); // Progress Monitor editbegin(); for(iCablePoint = 1; iCablePoint <= iPointCount; iCablePoint += 2) { vStartPoint = pointinfo(points[iCablePoint]); vEndPoint = pointinfo(points[iCablePoint + 1]); if(abs(normalize3D(vEndPoint - vStartPoint)) == <0.0,1.0,0.0>) { if(vEndPoint.y <= vStartPoint.y) {vEndPoint += <((random() * 2) - 1) * 0.00001,0.0,((random() * 2) - 1) * 0.00001>;} else {vStartPoint += <((random() * 2) - 1) * 0.00001,0.0,((random() * 2) - 1) * 0.00001>;} } nSegmentInc = 1 / iSegments; nSegment = 0.0; vStart = vStartPoint; vEnd = vEndPoint - vStartPoint; nRadiusMultiplier = nRadius + (random() * nRadiusRandom); nDroopMultiplier = (nDroop * distance3D(vStartPoint,vEndPoint)) + (random() * nDroopRandom * distance3D(vStartPoint,vEndPoint)); // Droop Noise vTurbulanceOffset =* nTurbulanceRandom; // Turbulance Variance aPointsCable[1] = vStartPoint; // Start Point aPointsCable[iSegments + 1] = vEndPoint; // End Point for(iCurrentPoint = 2; iCurrentPoint <= iSegments; iCurrentPoint++) // Middle Points { nSegment += nSegmentInc; // Multiplier nMultiplier = cos((nSegment * 3.1415926535) - 1.57079632675); // Droop aPointsCable[iCurrentPoint] = vStart + (vEnd * nSegment) + (vnGravity * nMultiplier * nDroopMultiplier); // Turbulance if(nTurbulance <> 0.0) { aPointsCable[iCurrentPoint] += cosinenoise3D((aPointsCable[iCurrentPoint] * nTurbulanceScale) + vTurbulanceOffset) * nMultiplier * nTurbulance; } // Noise if(nNoise <> 0.0) { aPointsCable[iCurrentPoint] += <(random() * 2) - 1,(random() * 2) - 1,(random() * 2) - 1> * nNoise; } } // Create Points // Start Point vPoint = aPointsCable[1]; vnForward = normalize3D(aPointsCable[2] - aPointsCable[1]); vnUp = normalize3D(crossproduct3D(vnForward,<0.0,1.0,0.0>)); vnRight = normalize3D(crossproduct3D(vnUp,vnForward)); aPolygonCable[1][1] = vPoint + (vnUp * nRadiusMultiplier) + (vnRight * nRadiusMultiplier); aPolygonCable[1][2] = vPoint + (vnUp * nRadiusMultiplier) + (vnRight * -nRadiusMultiplier); aPolygonCable[1][3] = vPoint + (vnUp * -nRadiusMultiplier) + (vnRight * -nRadiusMultiplier); aPolygonCable[1][4] = vPoint + (vnUp * -nRadiusMultiplier) + (vnRight * nRadiusMultiplier); // Mid Points for(iCurrentPoint = 2; iCurrentPoint <= iSegments; iCurrentPoint++) { vPoint = aPointsCable[iCurrentPoint]; vnForward = normalize3D(aPointsCable[iCurrentPoint + 1] - aPointsCable[iCurrentPoint - 1]); vnUp = normalize3D(crossproduct3D(vnForward,<0.0,1.0,0.0>)); vnRight = normalize3D(crossproduct3D(vnUp,vnForward)); aPolygonCable[iCurrentPoint][1] = vPoint + (vnUp * nRadiusMultiplier) + (vnRight * nRadiusMultiplier); aPolygonCable[iCurrentPoint][2] = vPoint + (vnUp * nRadiusMultiplier) + (vnRight * -nRadiusMultiplier); aPolygonCable[iCurrentPoint][3] = vPoint + (vnUp * -nRadiusMultiplier) + (vnRight * -nRadiusMultiplier); aPolygonCable[iCurrentPoint][4] = vPoint + (vnUp * -nRadiusMultiplier) + (vnRight * nRadiusMultiplier); } // End Point vPoint = aPointsCable[iSegments + 1]; vnForward = normalize3D(aPointsCable[iSegments + 1] - aPointsCable[iSegments]); vnUp = normalize3D(crossproduct3D(vnForward,<0.0,1.0,0.0>)); vnRight = normalize3D(crossproduct3D(vnUp,vnForward)); aPolygonCable[iSegments + 1][1] = vPoint + (vnUp * nRadiusMultiplier) + (vnRight * nRadiusMultiplier); aPolygonCable[iSegments + 1][2] = vPoint + (vnUp * nRadiusMultiplier) + (vnRight * -nRadiusMultiplier); aPolygonCable[iSegments + 1][3] = vPoint + (vnUp * -nRadiusMultiplier) + (vnRight * -nRadiusMultiplier); aPolygonCable[iSegments + 1][4] = vPoint + (vnUp * -nRadiusMultiplier) + (vnRight * nRadiusMultiplier); editend(); // 2 Point Poly Chain // Surface setsurface("DYNAMIC"); changesurface("DYNAMIC"); editbegin(); for(iCurrentPoint = 1; iCurrentPoint <= iSegments; iCurrentPoint++) { aPoints[1] = addpoint(aPointsCable[iCurrentPoint]); aPoints[2] = addpoint(aPointsCable[iCurrentPoint + 1]); addpolygon(aPoints); if(iCurrentPoint == 1) { mapFIXED.setValue(aPoints[1],1); } if(iCurrentPoint == iSegments) { mapFIXED.setValue(aPoints[2],1); } aPoints = nil; } editend(); // Polygon setsurface("Cable"); changesurface("Cable"); editbegin(); nDistance = 0.0; nCircumference = 3.1415926535 * (nRadiusMultiplier * 2); for(iCurrentPoint = 1; iCurrentPoint <= iSegments; iCurrentPoint++) { if(iUV == 1) // UV Segment { aUV[1] = 0.0; aUV[2] = 1.0; } else if(iUV == 2) // UV Object { aUV[1] = (1 / iSegments) * (iCurrentPoint - 1); aUV[2] = (1 / iSegments) * iCurrentPoint; } else if(iUV == 3) // UV Ratio (1:1) { aUV[1] = (1 / nCircumference) * nDistance; nDistance += distance3D(aPointsCable[iCurrentPoint],aPointsCable[iCurrentPoint + 1]); aUV[2] = (1 / nCircumference) * nDistance; } // 1 aPoints[1] = addpoint(aPolygonCable[iCurrentPoint + 1][1]); aPoints[2] = addpoint(aPolygonCable[iCurrentPoint][1]); aPoints[3] = addpoint(aPolygonCable[iCurrentPoint][2]); aPoints[4] = addpoint(aPolygonCable[iCurrentPoint + 1][2]); addpolygon(aPoints); mapUV.setValue(aPoints[1],@aUV[2],0.0@); mapUV.setValue(aPoints[2],@aUV[1],0.0@); mapUV.setValue(aPoints[3],@aUV[1],0.25@); mapUV.setValue(aPoints[4],@aUV[2],0.25@); // 2 aPoints[1] = addpoint(aPolygonCable[iCurrentPoint + 1][2]); aPoints[2] = addpoint(aPolygonCable[iCurrentPoint][2]); aPoints[3] = addpoint(aPolygonCable[iCurrentPoint][3]); aPoints[4] = addpoint(aPolygonCable[iCurrentPoint + 1][3]); addpolygon(aPoints); mapUV.setValue(aPoints[1],@aUV[2],0.25@); mapUV.setValue(aPoints[2],@aUV[1],0.25@); mapUV.setValue(aPoints[3],@aUV[1],0.5@); mapUV.setValue(aPoints[4],@aUV[2],0.5@); // 3 aPoints[1] = addpoint(aPolygonCable[iCurrentPoint + 1][3]); aPoints[2] = addpoint(aPolygonCable[iCurrentPoint][3]); aPoints[3] = addpoint(aPolygonCable[iCurrentPoint][4]); aPoints[4] = addpoint(aPolygonCable[iCurrentPoint + 1][4]); addpolygon(aPoints); mapUV.setValue(aPoints[1],@aUV[2],0.5@); mapUV.setValue(aPoints[2],@aUV[1],0.5@); mapUV.setValue(aPoints[3],@aUV[1],0.75@); mapUV.setValue(aPoints[4],@aUV[2],0.75@); // 4 aPoints[1] = addpoint(aPolygonCable[iCurrentPoint + 1][4]); aPoints[2] = addpoint(aPolygonCable[iCurrentPoint][4]); aPoints[3] = addpoint(aPolygonCable[iCurrentPoint][1]); aPoints[4] = addpoint(aPolygonCable[iCurrentPoint + 1][1]); addpolygon(aPoints); mapUV.setValue(aPoints[1],@aUV[2],0.75@); mapUV.setValue(aPoints[2],@aUV[1],0.75@); mapUV.setValue(aPoints[3],@aUV[1],1.0@); mapUV.setValue(aPoints[4],@aUV[2],1.0@); aPoints = nil; } // Cap End if(bCapEnd == 1) { editend(); setsurface("Cable (cross-section)"); changesurface("Cable (cross-section)"); editbegin(); // Start aPoints[1] = addpoint(aPolygonCable[1][4]); aPoints[2] = addpoint(aPolygonCable[1][3]); aPoints[3] = addpoint(aPolygonCable[1][2]); aPoints[4] = addpoint(aPolygonCable[1][1]); addpolygon(aPoints); mapUV.setValue(aPoints[1],@0.0,1.0@); mapUV.setValue(aPoints[2],@1.0,1.0@); mapUV.setValue(aPoints[3],@1.0,0.0@); mapUV.setValue(aPoints[4],@0.0,0.0@); // End aPoints[1] = addpoint(aPolygonCable[iSegments + 1][1]); aPoints[2] = addpoint(aPolygonCable[iSegments + 1][2]); aPoints[3] = addpoint(aPolygonCable[iSegments + 1][3]); aPoints[4] = addpoint(aPolygonCable[iSegments + 1][4]); addpolygon(aPoints); mapUV.setValue(aPoints[1],@0.0,1.0@); mapUV.setValue(aPoints[2],@1.0,1.0@); mapUV.setValue(aPoints[3],@1.0,0.0@); mapUV.setValue(aPoints[4],@0.0,0.0@); aPoints = nil; } monstep(); // Progress Monitor } editend(); monend(); // Progress Monitor // Selection - Point (GLOBAL) selmode(GLOBAL); iPointCountAfter = pointcount(); if((iPointCountAfter - iPointCountBefore) > 0) { for(s = 1; s <= (iPointCountAfter - iPointCountBefore) ; s++){aSelection[s] = s + iPointCountBefore;} selmode(USER); selpoint(SET, POINTNDX, aSelection); mergepoints(0.0); // Merge Points } // Undo undogroupend(); bChange = false; // Change // Done bDone = true; } // INTERPOLATION cosine1D: n1,n2,i // i = interpolation point (0-1) { i2 = (1 - cos(i * 3.1415926535)) * 0.5; return(n1 * (1 - i2) + n2 * i2); } // NOISE _noiseseed = 1; _noiseresolution = 512; _noise = nil; _noiseperm = nil; noiseinit { randomseed(_noiseseed); // Random Seed for(iN = 1; iN <= _noiseresolution; iN++) { _noise[iN] = (random() * 2) - 1; // -1.0..1.0 _noiseperm[iN] = wrap(1,_noiseresolution,integer(random() * _noiseresolution)); } } noiseseed: seed { _noiseseed = seed; noiseinit(); // Init } noiseresolution: resolution { _noiseresolution = resolution; noiseinit(); // Init } noiseperm1D: n // i { // ! if(_noiseperm == nil){noiseinit();} // Init return(_noiseperm[wrap(1,_noiseresolution,integer(n))]); } noiseperm2D: v // n { return(noiseperm1D(v.y + noiseperm1D(v.x))); } noiseperm3D: v // n { return(noiseperm1D(v.z + noiseperm2D(v))); } noise3D: v // n { // ! if(_noise == nil){noiseinit();} // Init return(_noise[noiseperm3D(v)]); } cosinenoise3D: v // n { v = wrap(1,_noiseresolution,v); vFrac = frac(v); vInt = v - vFrac; nA1 = noise3D( ); nA2 = noise3D( ); nA3 = noise3D( ); nA4 = noise3D( ); nA = cosine1D(cosine1D(nA1,nA2,vFrac.x),cosine1D(nA3,nA4,vFrac.x),vFrac.z); nB1 = noise3D( ); nB2 = noise3D( ); nB3 = noise3D( ); nB4 = noise3D( ); nB = cosine1D(cosine1D(nB1,nB2,vFrac.x),cosine1D(nB3,nB4,vFrac.x),vFrac.z); return(cosine1D(nA,nB,vFrac.y)); } // RANDOM _randomseed = 0; // n Seed randomseed: seed { _randomseed = seed; } random { n = (_randomseed * 214013 + 2531011) % 2^^24; _randomseed = n; n /= 2^^24; // 0..1 return(n); } // VECTOR 3D crossproduct3D: v1,v2 // v { // Vector return( ); } distance3D: v1, v2 // n { // Vector return(sqrt(((v2.x - v1.x) * (v2.x - v1.x)) + ((v2.y - v1.y) * (v2.y - v1.y)) + ((v2.z - v1.z) * (v2.z - v1.z)))); } magnitude3D: v // n { // Vector return(sqrt((v.x * v.x) + (v.y * v.y) + (v.z * v.z))); } normalize3D: v // v { // Vector normalize to 0 - 1 nMagnitude = magnitude3D(v); if(nMagnitude <> 0) nMagnitude = 1 / nMagnitude; return(v * nMagnitude); } // WRAP wrap: min,max,n { n = n - floor((n - min) / (max - min)) * (max - min); if(n < 0) n = n + max - min; // error check return(n); }
All scripts available at my Google Drive at
https://drive.google.com/open?id=1cR_q2GVUAJHumic1-A3eXV16acQnVTWs
Can you add the ability to make something other than square cable? Looks nice from far away but cables are normally round.
ReplyDeleteI originally wrote it to be converted to subdivision surfaces (SubD's) for rendering and animating. Hope that helps clear up why they are generated box like.
ReplyDeleteA small but useful stroke of genius. Thanks indeed.
ReplyDeleteSoooo very cool and much needed~ Thank you sir!
ReplyDeleteDefinitly usefull! Thank you for your work! I'm actualy making cables between buildings, good timing!
ReplyDeletegreat tool. thank you so much for sharing! is there a way to avoid the rotation of mesh? see screenshot here: http://www.3dworks.com/skitch/cable_tool-20140831-140950.jpg
ReplyDeletecheers
markus (3dworks on LW forums)
Great plugin, thank you
ReplyDelete