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)
- 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();
}
https://drive.google.com/open?id=1cR_q2GVUAJHumic1-A3eXV16acQnVTWs
No comments:
Post a Comment