User:Dora Gustafson/wind motor core

From Second Life Wiki
Jump to navigation Jump to search

Wind motor core

For use in virtual sailboats in SL

This wind motor uses four vectors to calculate boat velocity:

  1. v: boat velocity
  2. wind: wind velocity, direction at wind's eye
  3. head: boat heading, normalized vector
  4. n: sail normal, normalized vector. It doesn't matter which one of two normals is used
v = head∙(-n∙(v + wind)*n)*head

The tricky thing in a wind motor is that v is a function of v and it can not be isolated
Therefor v is calculated by iteration, i.e. repeated calculations using the previous result
Notes

  1. head and n are normalized vectors
  2. It does not matter if the positive or negative sail normal n is used in the equation
  3. wind is pointing the wind's eye so the wind blows opposite. Thus the minus sign in the equation
  4. The magnitude of wind is the wind speed
  5. The magnitude of v is the boat speed

A working LSL script based on this core

// wind motor core, minimal sailboat script by Dora Gustafson, Studio Dora 2016
// use a two prim object: root prim for hull and child prim for sail
// enter this script, rez on water, sit and sail
// up, down arrows for setting sail
// left, right arrows for steering

vector windVector = <.0, 9.0, .0>; // wind from north, 9 m/S
float WindFactor = 12.0; // m/S
float heelFactor = .3;
float threeDEG = 0.052360; // 3°
float MAXSAIL = 1.48353; // 85°
float MINSAIL = 0.174533; // 10°
float sailAngle;
vector linear_motor;
vector angular_motor;
integer sailPrim = 2;
integer hullPrim = 1;
float time = 4.0;
vector blue = <0.0, 0.455, 0.851>;
vector green = <0.18, 0.8, 0.251>;
vector yellow = <1.0, 0.863, 0.0>;
vector red = <1.0, 0.255, 0.212>;

default
{
    state_entry()
    {
        llSetVehicleType(VEHICLE_TYPE_BOAT);
        llSetVehicleFloatParam( VEHICLE_HOVER_HEIGHT, 0.001 );
        llSetVehicleFloatParam( VEHICLE_BANKING_EFFICIENCY, 0.0 );
        llSetVehicleFloatParam( VEHICLE_ANGULAR_MOTOR_TIMESCALE, .75); // default 4
        llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_EFFICIENCY, 1); // default 0.5
        llSetVehicleFloatParam( VEHICLE_LINEAR_DEFLECTION_TIMESCALE, .5); // default 3
        llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY, 0); // default 0.5
        llSetVehicleFloatParam( VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, .5); // default 5
        llSitTarget( <-1.5, .0, 1.25>, ZERO_ROTATION);
        sailAngle = MAXSAIL;
    }
    changed(integer change)
    {
        if (change & CHANGED_LINK)
        {
            key sitter = llAvatarOnSitTarget() ;
            if(sitter != NULL_KEY)
            {
                llSetStatus( STATUS_PHYSICS, TRUE);
                llRequestPermissions( sitter, PERMISSION_TAKE_CONTROLS);
                llSetTimerEvent( time);
            }
            else
            {
                llSetStatus( STATUS_PHYSICS, FALSE);
                llReleaseControls();
                llSetTimerEvent( .0);
            }
        }
    }
    run_time_permissions(integer perm)
    {
        if ( perm & PERMISSION_TAKE_CONTROLS ) llTakeControls(CONTROL_FWD | CONTROL_BACK | CONTROL_ROT_RIGHT | CONTROL_ROT_LEFT, TRUE, FALSE);
    }
    control(key id, integer level, integer edge)
    {
        if ( edge & level & ( CONTROL_FWD | CONTROL_BACK) )
        {
            if ( edge & level & CONTROL_FWD )
            {
                if ( sailAngle + threeDEG < MAXSAIL ) sailAngle += threeDEG;
            }
            else if ( edge & level & CONTROL_BACK)
            {
                if ( sailAngle - threeDEG > MINSAIL ) sailAngle -= threeDEG;
            }
            llSetTimerEvent( 0.05);
        }
        else
        {
            if ( level & (CONTROL_ROT_RIGHT)) angular_motor.z = 0.8;
            else if ( level & (CONTROL_ROT_LEFT)) angular_motor.z = -0.8;
            else angular_motor.z = 0.0;
            llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, angular_motor);
        }
    }
    timer()
    {
        if ( llGetStatus( STATUS_PHYSICS) )
        {
        // vector capture
            rotation sailRot = llGetRot();
            vector heading = llRot2Fwd( sailRot);
            vector left = llRot2Left( sailRot);
            vector velocity = llGetVel();
            float speed = llVecMag( velocity);
            vector Aw = windVector + velocity;
            Aw.z = 0.0;
            vector nAw = llVecNorm( Aw);
            vector axs = heading%nAw; // up or down axis to turn sail around
            float AwAngle = llAcos( heading*nAw); // apparent wind, local angle [0;PI]
        // setting sail
            float setsail = AwAngle;
            if ( setsail > sailAngle ) setsail = sailAngle;
            sailRot = llAxisAngle2Rot( axs, setsail);
            vector sailnormal = llRot2Left( sailRot)*llGetRot(); // Global
            llSetLinkPrimitiveParamsFast( sailPrim, [PRIM_ROT_LOCAL, sailRot'er]);
        // Linear and Angular Motors
            vector action = ZERO_VECTOR;
            if ( llFabs( sailnormal*nAw) > MINSAIL ) action = -(sailnormal*Aw)*sailnormal;
            linear_motor.x = WindFactor*action*heading;
            llSetVehicleVectorParam( VEHICLE_LINEAR_MOTOR_DIRECTION, linear_motor);
        // heel by wind
            angular_motor.x = -heelFactor*action*left;
            llSetVehicleVectorParam( VEHICLE_ANGULAR_MOTOR_DIRECTION, angular_motor);
            llSetTimerEvent( time);
        // optional signal color
            vector color = red;
            if ( sailAngle < 0.435*AwAngle ) color = blue;
            else if ( sailAngle < 0.565*AwAngle ) color = green;
            else if ( sailAngle < 0.825*AwAngle ) color = yellow;
            llSetLinkColor( sailPrim, color, ALL_SIDES);
        }
        else
        {
            llSetTimerEvent( 0.0);
        }
    }
    on_rez( integer p) { llResetScript(); }
}

A few boats in world use this core now. Two of mine are "Windy" and "Breezy 9'er"