User:Dora Gustafson/bezier toy
Jump to navigation
Jump to search
LSL Portal | Functions | Events | Types | Operators | Constants | Flow Control | Script Library | Categorized Library | Tutorials |
Bézier Toy
- Strictly for fun!
- The toy is made of one prim and a script.
- The prim will move along a smooth closed curve in space until it is stopped.
- The prim can be sat on and will then move you as a rollercoaster.
- Experimenting with the: camera distance, camera angle and camera in mouselook is great fun.
- Making a smoke tail is optional.
- The curve travelled
- The curve is made from a number of Bézier curves computed in the script.
- The curves are put together seamlessly.
- The points used for the curves are picked at random inside a box with editable size.
- New curves are computed each time The button New in the dialog menu is pressed.
- KeyFramed Motion
- The Second Life technique used is called: KeyFramed Motion
- This makes it possible to let the toy run forever without a script.
- Just start the toy and remove the script with the Kill Script button in the dialog menu
// Bezier Key Framed Motion script by Dora Gustafson, Studio Dora 2012
// Build on Cubic(3.order) Bezier chain by Dora Gustafson, Studio Dora 2008
// v1.01 Key Framed Motion 2012
// v1.02 introducing KFM
// v1.03 KFM alone
// v1.04 Appending more seamless bezier curves
// v1.05 Box limited randomP
// v1.06 Smoke on/off and script erase
// v1.07 Parameter menu
// v1.08 Particle size = f(prim scale). Range separate for X, Y and Z
// v1.09 Same randomP as in: "Bezier Spot Light Key Framed Motion". No more rangeOffset
// ... randomP box limits rotated as prim
float Tmotion = 8.0; // seconds
vector range = < 5.0, 5.0, 5.0 >; // ± half range for each coordinate in meters
rotation refFrame=<-0.5, -0.5, -0.5, 0.5>; // points Z forward
string toolDialog = "\nNew, Compute new points\nBegin Key Framed Motion\nEnd Key Framed Motion\nSmoke on or off\nKill Script to remove script from object\n...a Studio Dora product";
list TOOL_MENU = [ "CurveNumb", "Kill Script", "Range", "Cycle Time", "Frames/Curve", "New", "Begin", "End" ];
integer dialogChannel;
integer paramChannel;
integer paramHandl;
integer frames=16;
vector Po0;
vector Po1;
vector P0;
vector P1;
vector P2;
vector P3;
vector Q1;
vector Q2;
vector Q3;
vector LEFT=< 0.0, 1.0, 0.0 >;
list KFMlist=[];
vector b1;
vector b2;
rotation r1;
rotation r2;
integer curvNumb=3;
integer smoke=FALSE;
string partTextur;
rotation homeRot;
MakeParticles()
{
vector vsz = llGetScale();
float sc = 0.5*(vsz.x+vsz.y);
vsz = < sc, sc, 0.0 >;
llParticleSystem([
PSYS_PART_FLAGS,
PSYS_PART_INTERP_COLOR_MASK //Colors fade from start to end
| PSYS_PART_INTERP_SCALE_MASK //Scale fades from beginning to end
,PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_ANGLE_CONE
,PSYS_SRC_TEXTURE, partTextur //UUID of the desired particle texture, or inventory name
,PSYS_SRC_MAX_AGE, 0.0 //Time, in seconds, for particles to be emitted. 0 = forever
,PSYS_PART_MAX_AGE, 60.0 //Lifetime, in seconds, that a particle lasts
,PSYS_SRC_BURST_RATE, 0.02 //How long, in seconds, between each emission
,PSYS_SRC_BURST_PART_COUNT, 1 //Number of particles per emission
,PSYS_SRC_BURST_RADIUS, 0.0 //Radius of emission
,PSYS_SRC_BURST_SPEED_MIN, 0.0 //Minimum speed of an emitted particle
,PSYS_SRC_BURST_SPEED_MAX, 0.0 //Maximum speed of an emitted particle
,PSYS_SRC_ACCEL, <0.0,0.0,0.0> //Acceleration of particles each second
,PSYS_PART_START_COLOR, <1.0,1.0,1.0> //Starting RGB color
,PSYS_PART_END_COLOR, <1.0,1.0,1.0> //Ending RGB color, if INTERP_COLOR_MASK is on
,PSYS_PART_START_ALPHA, 1.0 //Starting transparency, 1 is opaque, 0 is transparent.
,PSYS_PART_END_ALPHA, 0.0 //Ending transparency
,PSYS_PART_START_SCALE, vsz //Starting particle size
,PSYS_PART_END_SCALE, 1.5*vsz //Ending particle size, if INTERP_SCALE_MASK is on
,PSYS_SRC_ANGLE_BEGIN, 3.1415 //Inner angle for ANGLE patterns
,PSYS_SRC_ANGLE_END, 3.1415 //Outer angle for ANGLE patterns
,PSYS_SRC_OMEGA, <0.0,0.0,0.0> //Rotation of ANGLE patterns, similar to llTargetOmega()
]);
}
openBox( integer n, string menuText)
{
paramChannel = dialogChannel + n;
llListenRemove( paramHandl);
paramHandl = llListen( paramChannel, "", llGetOwner(), "");
llTextBox( llGetOwner(), menuText, paramChannel);
}
vector randomP()
{
return llGetPos() + < range.x*(llFrand( 2.0)-1.0), range.y*(llFrand( 2.0)-1.0), range.z*(llFrand( 2.0)-1.0) > * homeRot;
}
rotation Vec2Rot( vector FWD )
{
FWD = llVecNorm( FWD );
vector UP = < 0.0, 0.0, 1.0 >;
if ( llFabs(FWD.z) < 1.0 ) LEFT = llVecNorm(UP%FWD);
UP = llVecNorm(FWD%LEFT);
return refFrame*llAxes2Rot(FWD, LEFT, UP);
}
tDialog()
{
string menuD = llGetScriptName( )+"\nMemory in use: "+(string)llGetUsedMemory();
menuD += "\n\nRange = ±"+(string)range+" meters";
menuD += "\nCycle time = "+(string)Tmotion+" Seconds";
menuD += "\nFrames per curve = "+(string)frames;
menuD += "\nNumber of curves = "+(string)curvNumb;
list T_MENU = ["Smoke ON"];
if (smoke) T_MENU = ["Smoke OFF"];
llDialog( llGetOwner(), menuD + toolDialog, T_MENU + TOOL_MENU, dialogChannel);
}
integer random_channel()
{// mask out 4 LSBits for other uses
return ((128 * (integer)llFrand( 8388608.0 )) + (integer)llFrand( 8388608.0 ) + 0x80000000) & 0xFFFFFFF0;
}
coefisFirst()
{
P0 = llGetPos();
Po0 = P0;
P3 = randomP();
P2 = randomP();
P1 = randomP();
Po1 = P1;
Q1 = 3.0*P1-3.0*P0;
Q2 = 3.0*P0-6.0*P1+3.0*P2;
Q3 = 3.0*P1-P0+P3-3.0*P2;
}
coefisNext()
{
P0 = P3;
P1 = 2.0*P3-P2;
P3 = randomP();
P2 = randomP();
Q1 = 3.0*P1-3.0*P0;
Q2 = 3.0*P0-6.0*P1+3.0*P2;
Q3 = 3.0*P1-P0+P3-3.0*P2;
}
coefisLast()
{
P0 = P3;
P1 = 2.0*P3-P2;
P2 = 2.0*Po0-Po1;
P3 = Po0;
Q1 = 3.0*P1-3.0*P0;
Q2 = 3.0*P0-6.0*P1+3.0*P2;
Q3 = 3.0*P1-P0+P3-3.0*P2;
}
vector Bez( float x ) { return P0 + (Q1 + (Q2 + Q3*x)*x)*x; }
vector dBez( float x ) { return Q1 + (2.0*Q2 + 3.0*Q3*x)*x; }
vector sBez( float x ) { return 2.0*Q2 + 6.0*Q3*x; }
default
{
state_entry()
{
llSetPrimitiveParams([PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX]);
dialogChannel = random_channel();
llListen( dialogChannel, "", llGetOwner(), "");
refFrame.s = -refFrame.s;
string textureName = llGetInventoryName(INVENTORY_TEXTURE, 0 );
if ( llGetInventoryType( textureName ) == INVENTORY_TEXTURE ) partTextur = textureName; // use extern, inventory texture
else partTextur = "";
}
touch_end(integer n)
{
if ( llDetectedKey(0) == llGetOwner() )
{
if ( llGetListLength( KFMlist) > 5 ) tDialog();
else llDialog( llGetOwner(), llGetScriptName( )+"\n\nMust make a new", ["New"], dialogChannel);
}
}
listen(integer channel, string name, key id, string message)
{
if ( message == "CurveNumb" ) openBox( 1, "Enter number of appended Bezier curves= 2,3,4,...,18");
else if ( message == "Range" ) openBox( 2, "Specify ±range relative to curent position\nCube: [X range, Y range, Z range]");
else if ( message == "Cycle Time" ) openBox( 3, "Time in seconds for one cycle");
else if ( message == "Frames/Curve" ) openBox( 4, "Number of key frames per Bezier curve [2;100]");
else if ( message == "Kill Script" ) llRemoveInventory( llGetScriptName());
else
{
if ( message == "New" )
{
homeRot = llGetRot();
float liN = (float)frames;
float dT = Tmotion/liN/(float)curvNumb;
dT = llRound(45.0*dT)/45.0;
if ( dT < 0.11111111 ) dT = 0.11111111;
KFMlist=[];
coefisFirst();
b1 = Bez(0);
r1 = Vec2Rot( dBez(0));
integer i;
for ( i=1; i<=frames; i++ )
{
b2 = Bez( i/liN); r2 = Vec2Rot(dBez( i/liN));
KFMlist += [ b2-b1, r2/r1, dT];
b1 = b2; r1 = r2;
}
integer j = curvNumb;
while ( j-- > 2 )
{
coefisNext();
for ( i=1; i<=frames; i++ )
{
b2 = Bez( i/liN); r2 = Vec2Rot(dBez( i/liN));
KFMlist += [ b2-b1, r2/r1, dT];
b1 = b2; r1 = r2;
}
}
coefisLast();
for ( i=1; i<=frames; i++ )
{
b2 = Bez( i/liN); r2 = Vec2Rot(dBez( i/liN));
KFMlist += [ b2-b1, r2/r1, dT];
b1 = b2; r1 = r2;
}
}
else if ( message == "End" )
{
llSetKeyframedMotion( [], []);
llSleep(0.2);
llSetRegionPos( Po0);
llSetPrimitiveParams([PRIM_POSITION, Po0, PRIM_ROTATION, homeRot]);
}
else if ( message == "Begin" && llGetListLength( KFMlist))
{
llSetKeyframedMotion( [], []);
llSleep(0.2);
llSetRegionPos( Po0);
llSetPrimitiveParams([PRIM_POSITION, Po0, PRIM_ROTATION, Vec2Rot( Po1-Po0)]);
llSetKeyframedMotion( KFMlist, [KFM_MODE, KFM_LOOP]);
}
else if ( message == "Smoke ON" ) { MakeParticles(); smoke = TRUE; }
else if ( message == "Smoke OFF" ) { llParticleSystem([]); smoke = FALSE; }
else if ( channel == dialogChannel + 1 )
{
curvNumb = (integer)message;
if ( curvNumb < 2 || curvNumb > 18 ) curvNumb = 2;
}
else if ( channel == dialogChannel + 2 )
{
list L = llCSV2List( message);
if ( llGetListLength(L) == 3 )
{
range.x = llList2Float( L, 0);
range.y = llList2Float( L, 1);
range.z = llList2Float( L, 2);
}
else if ( llGetListLength(L) == 1 ) range = (vector)llList2String( L, 0);
else if ( range.x<=0.0 || range.y<=0.0 || range.z<=0.0 ) llOwnerSay("Range must be entered as a vector with positive elements\nor as three comma separated positive numbers");
}
else if ( channel == dialogChannel + 3 )
{
Tmotion = (float)message;
if ( Tmotion < 1.0 ) Tmotion = 1.0;
}
else if ( channel == dialogChannel + 4 )
{
frames = (integer)message;
if ( frames < 2 || frames > 100 ) frames = 16;
}
llListenRemove( paramHandl );
tDialog();
}
}
on_rez(integer param) { llResetScript(); }
}
- New
- Will compute new curves from new randomly picked points in space
- The points will be picked inside a box with the prim in the center and rotated just like the prim
- Do not press New when the prim is moving, if you want any control of the curve
- Begin
- Begin the prim travel
- End
- End the travel and reset prim to start position and start rotation
- Range
- The range in which random points will be picked for the Bézier curves
- It is given by 3 coordinates X, Y and Z
- The coordinates form a box with the prim in the middle: prim position ±X, ±Y and ±Z
- This imaginary box is rotated just like the prim!
- In edit mode you can see the prim’s axes when you choose local coordinates (as opposed to world coordinates)
- Note that the prim will not stay inside the box on its journey, only the points used to compute the journey are guarantied to be inside
- Cycle Time
- The time it takes to complete one cycle from start to start
- Frames/Curve
- The number of keyframes for each Bézier curve
- Smoke ON/OFF
- Toggles the particle emitter ON/OFF
- CurveNumb
- The number of Bézier curves from start to start
- Kill Script
- Will remove the script and the toy can’t be controlled anymore
- The toy will continue doing what it did when the script was removed