CameraSynch2

From Second Life Wiki
Jump to navigation Jump to search

CameraSynch2 v1.0.0

Synchronization of multiple avatar cameras.
This script initially needs to be dropped individually into numerous linked prims.
However because the script adds a PIN, after its been added once the owner can issue the "update" command to automatically propogate any changes made to the root script into the child prims/scripts.

TO USE:
1] Rez a bunch of prims, one for each bum you want to sit on them.
2] Link them together.
3] Drop a copy of this script in each prim.
4] Sit one person on the controller seat, and some persons on the other seats.
5] Read the spam when you sit down.

//
// BELOG Hax
// AEST 20071221 0452 [SLT:  20071220 1152]
//  https://wiki.secondlife.com/wiki/CameraSynch2
//
// VERSION:
//  v1.0.0 revised and wikified from my previous: 'CameraSynch v0.3.1'
//      Not really well tested, but seems good. 
//      Timer frequency could do with some moderation... Possibly with dynamic adjustment.
//
// CREDITS:
//  Meyermagic Salome and Nomad Padar for
//  http://wiki.secondlife.com/wiki/Camera_Sync
//  I drew some inspiration from theirs some time after I wrote the first iteration of this script.
//  I don't think I actually derived anything from it... 
//  but theirs was the original..and Nomad is awesome.
//=========================================================================
// ---LICENCE START---
// http://creativecommons.org/licenses/by-sa/3.0/
// ie: Attribution licence:
//   Give me credit by leaving it in the script I created.
//   Supply my original script with your modified version.
//   Refer to the wiki URL from which you copied this script.
//      https://wiki.secondlife.com/wiki/CameraSynch2
// ---LICENCE END---
//=========================================================================

// SHARED CONFIGURATION
integer     gPayloadPIN         = 89876847;
//----------------------------------
// CONFIGURATION
float       gFrequency          = 0.02;
integer     gChan               = 0;
//----------------------------------
// CORE CODE - controller
string      gCamController              = NULL_KEY;
vector      gOldCamPos;
rotation    gOldCamRot;
string      gOldDesc;
integer     gTog;
// CORE CODE -  spectator
//--------------------------------------
f_camDefault()
{   llSetCameraParams([
        CAMERA_ACTIVE, FALSE, //1 is active, 0 is inactive
        CAMERA_BEHINDNESS_ANGLE, 10.0, //(0 to 180) degrees
        CAMERA_BEHINDNESS_LAG, 0.0, //(0 to 3) seconds
        CAMERA_DISTANCE, 3.0, //(0.5 to 10) meters
        CAMERA_FOCUS_LAG, 0.1 , //(0 to 3) seconds
        CAMERA_FOCUS_LOCKED, FALSE, //(TRUE or FALSE)
        CAMERA_FOCUS_THRESHOLD, 1.0, //(0 to 4) meters
        CAMERA_PITCH, 0.0, //(-45 to 80) degrees
        CAMERA_POSITION_LAG, 0.1, //(0 to 3) seconds
        CAMERA_POSITION_LOCKED, FALSE, //(TRUE or FALSE)
        CAMERA_POSITION_THRESHOLD, 1.0, //(0 to 4) meters
        CAMERA_FOCUS_OFFSET, ZERO_VECTOR //<-10,-10,-10> to <10,10,10> meters
    ]);
}
//----------------------------------
default
{   
    on_rez(integer start_param)
    {   llResetScript();
    }    

    state_entry()
    {   if (gPayloadPIN)    llSetRemoteScriptAccessPin(gPayloadPIN);
        llSitTarget(<0.2, 0.0, 0.5>, <0.0, 0.0, 0.0, 1.0>);                 
        integer link = llGetLinkNumber();
        if (link>1)
        {   string name = "Spectator#"+(string)(link-1);
            llSetObjectName(name);
            llSetColor(<0.0, 1.0, 1.0>, ALL_SIDES);            
            llSetText(name, <1.0, 1.0, 1.0>, 1.0);
            state repeater;
        }
        llSetColor(<1.0, 1.0, 0.0>, ALL_SIDES);
        llSetObjectName(llGetScriptName());        
        llSetText("CONTROLLER", <1.0, 1.0, 1.0>,1.0);        
        if (gPayloadPIN)
            llListen(gChan, "", llGetOwner(), "update");        
    }

    listen(integer channel, string name, key id, string message)
    {   integer cc;
        integer c = cc = llGetNumberOfPrims();
        llOwnerSay("WAIT: Propogating scripts...");    
        while(c>1)
        {   llSetText("Updating: "+(string)(cc-c+1)+"/"+(string)(cc-1)
                , <1.0, 1.0, 1.0>, 1.0);
            llRemoteLoadScriptPin(llGetLinkKey(c--), llGetScriptName(), gPayloadPIN, TRUE, 1);   
        }
        llSetText("CONTROLLER", <1.0, 1.0, 1.0>, 1.0); 
        llOwnerSay("OK: Script propogation completed.");
    }

    changed(integer change)
    {   if (change & CHANGED_LINK)
        {   gCamController = llAvatarOnSitTarget();
            if (gCamController != NULL_KEY)
            {   llRequestPermissions(gCamController
                    , PERMISSION_TRACK_CAMERA|PERMISSION_TAKE_CONTROLS
                );
            }
            else
            {   llSetTimerEvent(0.0);
                llSetObjectDesc(gOldDesc);
                llResetScript();
            }
        }
    }

    run_time_permissions(integer perm)
    {   if(perm & PERMISSION_TAKE_CONTROLS)        
        {   llTakeControls(0| CONTROL_LBUTTON,TRUE, TRUE); //work anywhere
            llInstantMessage(llGetPermissionsKey(), 
                "\nThis device allows the spectator cameras to be controlled by YOU."
                +"\nUseful Tips:"
                +"\n1] Ensure you are NOT in mouselook."
                +"\n2] Spectators cameras track yours with a short delay, and do not move quite as smoothly "
                +"as yours does, so go slowly and avoid any sudden or disconcerting movements."
                +"\nREMEMBER: If your drawdistance is greater than a spectators, their camera may be left "
                +"behind at some point. So if you hear people say 'did we stop moving?' you should move "
                +"your camera closer to where you are all sitting."
            );
            llSetTimerEvent(gFrequency);
        }
        else //never
            llUnSit(gCamController);
    }

    timer()
    {   vector camPos = llGetCameraPos();
        rotation camRot = llGetCameraRot();
        if (llVecDist(camPos, gOldCamPos) > 0.001 
        || llAngleBetween(camRot, gOldCamRot) >= 0.0008)
        {   gTog = !gTog;
            gOldCamPos = camPos;
            gOldCamRot = camRot;
            llSetText("RELAYING..",<0.0, (float)gTog, 1.0-gTog>,1.0);
            llSetObjectDesc((string)camPos+"#"+(string)camRot);      
        }
    }
}
//=========================================================================
state repeater
{   
    on_rez(integer start_param)
    {   llResetScript();
    }

    state_entry()
    {   llSetTimerEvent(0.0);
    }

    changed(integer change)
    {   if (change & CHANGED_LINK)
        {   if (llGetLinkNumber() == 0)
                llResetScript();
            key av = llAvatarOnSitTarget();
            if (av != NULL_KEY && llGetPermissionsKey() == NULL_KEY)
            {   llRequestPermissions(av, PERMISSION_CONTROL_CAMERA);
            }
        }
    }

    run_time_permissions(integer perms)
    {   if (perms & PERMISSION_CONTROL_CAMERA)
        {   llInstantMessage(llGetPermissionsKey(), 
                "This device allows the spectator cameras to be centrally controlled."
                +"\nPlease : "
                +"\n1] Ensure you are NOT in mouselook."
                +"\n2] Reset your camera by pushing the escape button 3 or 4 times."
                +"\nNOTE: If the controller avatar's drawdistance is greater than yours, "
                +"you may experience being 'left behind' at some point. Either increase "
                +"your draw distance, or reset your camera again  (hit escape a few times) "
                +"after a short while and hopefully the controllers camera is closer and "
                +"tracking will resume.\nObviously it helps to let the controller know if "
                +"they are too far away, but perhaps your drawdistance could also be higher."
                +"\n[ctrl-P.... graphics tab...'custom' checkbox... 'drawdistance' slider... increase it a bit]"
            );
            llSetTimerEvent(gFrequency);
        }
    }

    timer()
    {   if (llGetPermissionsKey())
        {   string desc = llList2String(llGetObjectDetails(llGetLinkKey(LINK_ROOT), [OBJECT_DESC]),0); 
            integer index = llSubStringIndex(desc, "#");
            vector pos = (vector)llGetSubString(desc, 0, index-1);
            if (pos == ZERO_VECTOR)
            {   f_camDefault();
                return;
            }
            rotation rot = (rotation)llGetSubString(desc, index+1, -1);
            llSetCameraParams([
                CAMERA_ACTIVE, 1 //1 is active, 0 is inactive
                ,CAMERA_BEHINDNESS_ANGLE, 0.0 //(0 to 180) degrees
              //,CAMERA_BEHINDNESS_LAG, 0.05 //(0 to 3) seconds
                ,CAMERA_DISTANCE, 0.0 //(0.5 to 10) meters
                ,CAMERA_FOCUS, pos + llRot2Fwd(rot) //Region-relative
                ,CAMERA_FOCUS_LAG, 0.05 //(0 to 3) seconds
                ,CAMERA_FOCUS_LOCKED, TRUE //(TRUE or FALSE)
                ,CAMERA_FOCUS_THRESHOLD, 0.0 //(0 to 4) meters
                ,CAMERA_POSITION, pos //Region-relative position
                ,CAMERA_POSITION_LAG, 0.05 //(0 to 3) seconds
                ,CAMERA_POSITION_LOCKED, TRUE //(TRUE or FALSE)
                ,CAMERA_POSITION_THRESHOLD, 0.0 //(0 to 4) meters
                ,CAMERA_FOCUS_OFFSET, ZERO_VECTOR //<-10,-10,-10> to <10,10,10> meters
            ]);
        }
        else
        {   llSetTimerEvent(0.0);
        }
    }
}
//=========================================================================