Feedback

Please leave feedback and comments. I am always interested to hear how people get on using these LScripts!

Friday 10 February 2012

LScript - Modeler_Cable


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