Feedback

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

Wednesday 24 September 2014

LScript - Custom_Shadow


LScript (Layout) adds a shadow visual aid to Layout. It is purely a visual cue for objects positioned or sitting on the ground plane.

There are three types available
  • Simple - Basic distance from ground plane
  • Simple (Projected) - Projected style shadow (experimental)
  • Object (Projected) - Projected shadow of object (experimental)
Changes
  • Error found by erikalst has now been fixed
  • Shadow will nolonger disappear if object drops below ground plane

Compatible with Newtek LightWave 9.6 and above.

// LScript Custom Object - www.StephenCulley.co.uk
//
// web   address: http://www.stephenculley.co.uk
// email address: email@stephenculley.co.uk

/*  
    LScript Custom Object - Shadow

    Custom_Shadow.ls

*/

@version 2.4
@warnings
@script custom
@name *Shadow

    // Title
    sTitle = "*Shadow";

    // Version
    sVersion = "v1.0";  

    // Items
    Item; // Item
    LightItem = nil; // Item
    LightItemName = "nil";

    bCreate; // Create
    iSteps = 32; // Circle detail
    sType = @"Simple","Simple (Projected)","Object (Projected)"@;
    iType = 1; // 1 = Simple, 2 = Complex
    nDegStep = 360 / iSteps; // Degree per step
    nOffset = 0.0001;
    nTime; // Time

    vColor = <75.0,75.0,75.0>; // Color;

create: ma
{
    bCreate = false; // Create

    setdesc(sTitle + " " + sVersion + " - " + sType[iType]);

    Item = ma; // Item
}

destroy
{
}

newtime: frame, time
{
    nTime = time;
}

init
{
}

process: ca
{
    // Create - invalid argument fix
    if(bCreate == false){bCreate = true; return;}

    // Item
    if(Item)
        {
        vnRight = normalize3D(Item.getRight(nTime));
        vnUp = normalize3D(Item.getUp(nTime));
        vnForward = normalize3D(Item.getForward(nTime));
        vWorldPosition = Item.getWorldPosition(nTime);
        vScaling = Item.getScaling(nTime);

        aBoundingBox = Item.position(getlayernumber(Item));

        nSize = ((aBoundingBox[2].x - aBoundingBox[1].x) +
                 (aBoundingBox[2].y - aBoundingBox[1].y) +
                 (aBoundingBox[2].z - aBoundingBox[1].z)) * vScaling * 0.333333; // Size of object averaged

        nHeight = (aBoundingBox[2].y - aBoundingBox[1].y) * vScaling.y;

        nBottom = min(vWorldPosition.y - (aBoundingBox[2].y * vScaling.y),vWorldPosition.y - (aBoundingBox[1].y * vScaling.y));
        }
    else
        {
        vnRight = <0.0,0.0,0.0>;
        vnUp = <0.0,0.0,0.0>;
        vnForward = <0.0,0.0,0.0>;
        vWorldPosition = <0.0,0.0,0.0>;
        vScaling = <0.0,0.0,0.0>;
        nHeight = 0.0;
        nSize = 0.0000001;
        }


    // Light Item
    if(LightItemName != "nil") {LightItem = Light(LightItemName); LightItemName = "nil";}
    if(LightItem)
        {
        vLight = LightItem.getWorldPosition(nTime);
        }
    else
        {
        TempItem = Light(1);
        vLight = TempItem.getWorldPosition(nTime);
        }
    if(vLight.y < 0.0) return; // Break

// Simple

    if(iType == 1) // Type 1 = Simple
      {
      nRadius = (nSize * 0.5) * (1 - ((1 / nSize * 0.5) * max(0.0,(vWorldPosition.y - (nHeight * 0.5)) )));
      if(nRadius < 0.0) return; // Check

      // Calculate circle
      nDegree = 0;
      iPoints = 1;

      for(d = 0; d <= iSteps; d++)
        {
        vPoint = <(cos(DEGtoRAD(nDegree)) * nRadius),nOffset,(sin(DEGtoRAD(nDegree)) * nRadius)>;
        aPoints[iPoints] = vPoint + ; // Store vertex
        iPoints++; // Count
        nDegree += nDegStep; // Add step
        }

      aPoints[iPoints] = aPoints[1]; // Add copy of first point

      // Draw
      ca.setColor(vColor * (1/255)); // Set color
      for(p = 1; p < iPoints; p++)
        {
        ca.drawTriangle(,aPoints[p],aPoints[p + 1],SYS_WORLD);
        }  

      }

// Simple (Projected)

    if(iType == 2)
      {
      // Circle center
      aIntersect = lineplaneintersect3D(vLight,normalize3D(vWorldPosition - vLight),<0.0,0.0,0.0>,<0.0,1.0,0.0>);
      if(aIntersect[1] == false) return; // Break No shadow
      vCenter = aIntersect[3] + <0.0,nOffset,0.0>;

      aOrthonormal[1] = normalize3D(vLight - vWorldPosition);
      vPoint = pointplane3D(vWorldPosition + <1.0,0.0,1.0>,vWorldPosition,normalize3D(vWorldPosition - vLight));
      aOrthonormal[2] = normalize3D(vPoint - vWorldPosition);       
      aOrthonormal[3] = normalize3D(crossproduct3D(aOrthonormal[2],aOrthonormal[1]));

      nRadius = nSize * 0.5; // Radius
      nDegree = 0;
      iPoints = 1;

      for(d = 0; d <= iSteps; d++)
        {
        vPoint = (aOrthonormal[2] * (cos(DEGtoRAD(nDegree)) * nRadius)) + 
                 (aOrthonormal[3] * (sin(DEGtoRAD(nDegree)) * nRadius)) + vWorldPosition;
        aIntersect = lineplaneintersect3D(vLight,normalize3D(vPoint - vLight),<0.0,0.0,0.0>,<0.0,1.0,0.0>);
        aPoints[iPoints] = aIntersect[3] + <0.0,nOffset,0.0>; // Store vertex
        iPoints++; // Count
        nDegree += nDegStep; // Add step
        }

      aPoints[iPoints] = aPoints[1]; // Add copy of first point

      // Draw
      ca.setColor(vColor * (1/255)); // Set color
      for(p = 1; p < iPoints; p++)
        {
        ca.drawTriangle(vCenter,aPoints[p],aPoints[p + 1],SYS_WORLD);
        }  
      }

// Object (Projected)

    if(iType == 3)
      {
      if(Item == nil){return;}

      for(p = 1; p <= Item.polygonCount(); p++)
        {
        aPoints = nil;
        for(v = 1; v <= Item.vertexCount(Item.polygons[p]); v++)
          {
          vPoint = Item.position(Item.vertex(Item.polygons[p],v));
          vPoint = (vnRight * vPoint.x * vScaling.x) +
                   (vnUp * vPoint.y * vScaling.y) +
                   (vnForward * vPoint.z * vScaling.z) + vWorldPosition;

          aIntersect = lineplaneintersect3D(vLight,normalize3D(vPoint - vLight),<0.0,0.0,0.0>,<0.0,1.0,0.0>);
          aPoints[v] = aIntersect[3]; 
          }

        // Draw
        ca.setColor(vColor * (1/255)); // Set color
        if(Item.vertexCount(Item.polygons[p]) == 3)
          {
          ca.drawTriangle(aPoints[1],aPoints[2],aPoints[3],SYS_WORLD);             
          }
        else
          {
          ca.drawTriangle(aPoints[1],aPoints[2],aPoints[3],SYS_WORLD);             
          ca.drawTriangle(aPoints[1],aPoints[3],aPoints[4],SYS_WORLD);             
          }
      
        }
      }

}

// CONVERSIONS

DEGtoRAD: n // Degree to radian
{
    return(n * (3.1415926535 / 180));
}

RADtoDEG: n // Radian to degree
{
    return(n * (180 / 3.1415926535));
}

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

dotproduct3D: v1,v2 // n
{
    // Vector
    return(v1.x * v2.x + v1.y * v2.y + v1.z * v2.z);
}

lineplanedistance3D: v1,vn1,v2,vn2 // n
{
    // Vector - v1 v2 is Vector / vn1 vn2 is Vector Normal
    nNumerator = dotproduct3D(vn2,v1) + dotproduct3D(-v2,vn2);
    nDenomimator = dotproduct3D(vn2,vn1);
    if(nDenomimator <> 0.0){return(-(nNumerator / nDenomimator));} else{return(0.0);}
}

lineplaneintersect3D: v1,vn1,v2,vn2 // a[1] b (true / false)
                                    // a[2] n (distance)
                                    // a[3] v (vector)
{
    // Vector - v1 v2 is Vector / vn1 vn2 is Vector Normal
    nNumerator = dotproduct3D(vn2,v1) + dotproduct3D(-v2,vn2);
    nDenomimator = dotproduct3D(vn2,vn1);
    if(nDenomimator <> 0.0){nDistance =  -(nNumerator / nDenomimator);} else{ nDistance = 0.0;}
    if(nDistance >= 0.0){a[1] = true;}else{a[1] = false;} // True / False
    a[2] = nDistance; // Distance
    a[3] = v1 + (vn1 * nDistance); // Vector
    return(a);
}

normal3D: v1,v2 // vn
{
    // Vector
    return();
}

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);
}

orthonormal3D: vn // a[1] vn (vector normal)
                  // a[2] vn (vector normal)
                  // a[3] vn (vector normal)
{
    // Orthonormal Vectors from Vector Normal
    a[1] = vn;
    if(vn.x |= vn.y)
        {
        nMagnitude = 1 / sqrt(vn.x * vn.x + vn.z * vn.z);
        a[2] = <-vn.z * nMagnitude,0.0,vn.x * nMagnitude>;
       }
    else
        {
        nMagnitude = 1 / sqrt(vn.y * vn.y + vn.z * vn.z);
        a[2] = <0.0,vn.z * nMagnitude,-vn.y * nMagnitude>;
        }
    a[3] = crossproduct3D(a[1],a[2]);
    return(a);
}

pointplane3D: v1,v2,vn2 // v
{
    // Vector - v1 is Vector / v2 is Vector on Plane / vn2 is Vector Normal on Plane
    return(v1 + (dotproduct3D(vn2,v2) - dotproduct3D(vn2,v1)) * vn2);
}

sqrlinemagnitude3d: v1,v2 // n
{
    // Vector square magnitude of v1 to v2
    return((v2.x - v1.x) * (v2.x - v1.x) + (v2.y - v1.y) * (v2.y - v1.y));
}

getlayernumber: item
{
    if(item.totallayers == 1){return(1);}
    sTokens = parse(":",item.name);
    iLayer = 0;
    if (strleft(sTokens[2],5)=="Layer")
      {
      iLayer = integer(sTokens[2]);
      }
    else
      {
      iTotal = item.totallayers;
      for(i = 1; i <= iTotal; i++)
        {
        if(item.layerVisible(i) == 1)
          {
          if(item.layerName(i) == sTokens[2])
            {
            iLayer = i;
            break;
            }
          }
        else
          {
          iTotal++;
          }
        }
      }
  return iLayer;
}

load: what,io
{
    if(what == SCENEMODE)   // processing an ASCII scene file
    {
        iType = io.read().asInt();
        vColor = io.read().asVec();
        nOffset = io.read().asNum();
        LightItemName = io.read().asStr();

        setdesc(sTitle + " " + sVersion + " - " + sType[iType]);
    }
}

save: what,io
{
    if(what == SCENEMODE)
    {
        io.writeln(iType);
        io.writeln(vColor);
        io.writeln(nOffset);

        if(LightItem != nil)
            {
            io.writeln(string(LightItem.name));
            }
        else
            {
            io.writeln("nil");
            }  
    }
}

options
{
    reqbegin(sTitle + " " + sVersion);

    ctrl_c0 = ctlchoice("Type",iType,@"Simple","Simple (Projected)","Object (Projected)"@);
    ctrl_c1 = ctlcolor("Color",vColor);
    ctrl_c2 = ctlnumber("Offset",nOffset);

    ctlsep();
    ctrl_l0 = ctllightitems("Light Item",LightItem);

    // Developer
    ctlsep();
    ctrl_dev0 = ctltext("","developer: Stephen Culley","http://www.stephenculley.co.uk");

    return if !reqpost();

    iType = getvalue(ctrl_c0);
    vColor = getvalue(ctrl_c1);
    nOffset = getvalue(ctrl_c2);
    LightItem = getvalue(ctrl_l0);

        setdesc("*Shadow - " + sType[iType]);

    reqend();
}
All scripts available at my Google Drive at
https://drive.google.com/open?id=1cR_q2GVUAJHumic1-A3eXV16acQnVTWs

No comments:

Post a Comment