Accessing Prim Properties

From OpenSimulator

(Difference between revisions)
Jump to: navigation, search
m
m (Example added)
Line 63: Line 63:
 
| llGet/SetPrimitiveParams || llGet/SetLinkPrimitiveParams(num) || osGet/SetPrimitiveParams(uuid)
 
| llGet/SetPrimitiveParams || llGet/SetLinkPrimitiveParams(num) || osGet/SetPrimitiveParams(uuid)
 
|}
 
|}
 +
<br/>
  
 +
==Example : A replicator==
 +
Put this script in a box, along with a single-prim object named "seed". It will iterate on each prim of a named object you own in the scene, then rez an identical prim at a given offset, perfectly replicating the source object, even meshes. Sleeps are here for demo. Remove them for speedy replication. CLEANUP option will get rid of rezed prims.
 +
<source lang="lsl">
 +
string targetName = "Example"; // The name of an object you own in the scene
 +
string seedName = "seed";      // Name of the seed prim
 +
vector offset = <0,0,1>;      // Offset of the dupicata
 +
 +
integer primcnt;
 +
integer primnum;
 +
key rootKey;
 +
 +
integer TupleLength (integer param)
 +
{
 +
    if (param == PRIM_GLOW)      return 1;
 +
    if (param == PRIM_COLOR)      return 2;
 +
    if (param == PRIM_TEXGEN)    return 1;
 +
    if (param == PRIM_TEXTURE)    return 4;
 +
    if (param == PRIM_NORMAL)    return 4;
 +
    if (param == PRIM_SPECULAR)  return 7;
 +
    if (param == PRIM_BUMP_SHINY) return 2;
 +
    if (param == PRIM_FULLBRIGHT) return 1;
 +
 +
    return 0; // Not a by-side parameter
 +
}
 +
 +
list IterateFaces (list p, integer param)
 +
{
 +
    integer tuplelength = TupleLength (param);
 +
    integer numsides =  llGetListLength(p) / tuplelength;
 +
 +
    // Une seule face, retourner [ param, ALL_SIDES, p ]
 +
    if (numsides == 1) return [ param, ALL_SIDES ] + p;
 +
 +
    list result = [ ];
 +
    for (integer s=0; s<numsides; s++) {
 +
        list tuple = llList2List (p, s*tuplelength, (s+1)*tuplelength-1);
 +
        result += [ param, s ] + tuple;
 +
    } 
 +
    return result;
 +
}
 +
 +
CopyParams (key srcprim, key dstprim, integer param) {
 +
    if (TupleLength(param) == 0)
 +
    {
 +
        list p = osGetPrimitiveParams (srcprim, [ param ]);
 +
        if (param == PRIM_POSITION) p = [ llList2Vector (p,0) + <5,0,0> ];
 +
        osSetPrimitiveParams (dstprim, [ param ] + p);
 +
    }
 +
    else
 +
    {
 +
        list p = osGetPrimitiveParams (srcprim, [ param, ALL_SIDES ]);
 +
        p = IterateFaces (p, param);
 +
        osSetPrimitiveParams (dstprim, p);
 +
    }
 +
    llSleep (0.1);
 +
}
 +
 +
RezAPrim ()
 +
{
 +
    if (++primnum > primcnt) llResetScript();
 +
    llRezObject (seedName, llGetPos()+offset, ZERO_VECTOR, ZERO_ROTATION, 0);
 +
}
 +
 +
PrimRezed (key dstkey)
 +
{
 +
    llOwnerSay ("Processing link "+(string)primnum+" of "+(string)primcnt);
 +
    key srckey = llGetObjectLinkKey (rootKey, primnum);
 +
    llSleep (0.1);
 +
   
 +
    CopyParams (srckey, dstkey, PRIM_POSITION);
 +
    CopyParams (srckey, dstkey, PRIM_ROTATION);
 +
    CopyParams (srckey, dstkey, PRIM_TYPE);
 +
    CopyParams (srckey, dstkey, PRIM_SIZE);
 +
    CopyParams (srckey, dstkey, PRIM_COLOR);
 +
    CopyParams (srckey, dstkey, PRIM_TEXTURE);
 +
    CopyParams (srckey, dstkey, PRIM_NORMAL);
 +
    CopyParams (srckey, dstkey, PRIM_SPECULAR);
 +
    CopyParams (srckey, dstkey, PRIM_TEXGEN);
 +
    CopyParams (srckey, dstkey, PRIM_BUMP_SHINY);
 +
    CopyParams (srckey, dstkey, PRIM_FULLBRIGHT);
 +
    CopyParams (srckey, dstkey, PRIM_PHYSICS_SHAPE_TYPE);
 +
 +
    RezAPrim ();
 +
}
 +
 +
 +
key OwnerOf (key id)
 +
{
 +
    list l = llGetObjectDetails (id, [ OBJECT_OWNER ]);
 +
    return llList2Key (l, 0);
 +
}
 +
 +
 +
default
 +
{
 +
    state_entry()
 +
    {
 +
        llSetText ("READY", <0,1,0>, 1.0);
 +
    }
 +
 +
    touch_start (integer n)
 +
    {
 +
        state dialog;
 +
    }
 +
}
 +
 +
 +
integer dlgchan = 99;
 +
integer dlglistener;
 +
state dialog
 +
{
 +
    state_entry()
 +
    {
 +
        llSetText ("LISTENING", <0,1,0>, 1.0);
 +
        llDialog (llGetOwner(), "Action?", ["CLEANUP", "REPLICATE"], dlgchan);
 +
        dlglistener = llListen (dlgchan, "", llGetOwner(), "");
 +
        llSetTimerEvent (10);
 +
    }
 +
 +
    listen (integer chan, string name, key id, string msg)
 +
    {
 +
        if (msg == "CLEANUP")  state cleanup;
 +
        if (msg == "REPLICATE") state replicate;
 +
    }
 +
 +
    timer ()
 +
    {
 +
        state default;
 +
    }
 +
 +
    state_exit ()
 +
    {
 +
        llSetTimerEvent (0.0);
 +
        llListenRemove (dlglistener);
 +
    }
 +
}
 +
 +
 +
state cleanup
 +
{
 +
    state_entry()
 +
    {
 +
        llSetText ("CLEANUP", <0,1,0>, 1.0);
 +
        llSensor (seedName, NULL_KEY, PASSIVE|SCRIPTED, 100, PI);
 +
    }
 +
 +
    sensor (integer num) {
 +
        llOwnerSay ("Garbage collecting "+(string)num);
 +
        for (integer i=0; i<num; i++) osDie (llDetectedKey(i));
 +
        state default;
 +
    }
 +
 +
    no_sensor() {
 +
        llOwnerSay ("All clean.");
 +
        state default;
 +
    }
 +
}
 +
 +
 +
state replicate
 +
{
 +
    state_entry()
 +
    {
 +
        llSetText ("REPLICATE", <0,1,0>, 1.0);
 +
        llSensor (targetName, NULL_KEY, PASSIVE|SCRIPTED, 100, PI);
 +
    }
 +
 +
    sensor (integer num) {
 +
        rootKey = llDetectedKey (0);
 +
 +
        if (OwnerOf(rootKey) != llGetOwner())
 +
        {
 +
            llOwnerSay ("I don't own "+targetName+". Aborting.");
 +
            state default;
 +
        }
 +
 +
        primcnt = llGetObjectPrimCount (rootKey);
 +
        primnum = 0; RezAPrim ();
 +
    }
 +
 +
    no_sensor() {
 +
        llOwnerSay ("Object "+targetName+" was not found.");
 +
    }
 +
   
 +
    object_rez (key uuid) {
 +
        PrimRezed (uuid);
 +
    }
 +
}
 +
</source>
  
 
==Writing accessors==
 
==Writing accessors==

Revision as of 13:02, 1 March 2024

A bit of history

First, there was llSetColor.

Then, there was llSetLinkColor. That was a huge step forward, since it was releaving you from the burden to script each and every prim in your hair. Don't laugh. You may still find such blatantly outdated scripts on the Hypergrid. Do us a favor. Don't propagate them. Trash them. Rewrite them.

Later, it was recognized that the ability to access every property of a prim in a linkset from a master script was a Good Thing. It makes your application simpler to understand and to maintain, and helps reduce script count. llSetLinkPrimitiveParams was born.

Finally, OpenSimulator added it's own touch. Why not manipulate prims outside the linkset from a master script ? And we had osSetPrimitiveParams.

llGet/SetLinkPrimitiveParams, osGet/SetPrimitiveParams are the workhorse of prim manipulations. You can access every existing property of a prim from a centralized location. Streamlined, faster code. No more need for chat, or LinkedMessages between prims.

A special note about llGetObjectLinkKey : Introduced September, 2022, this function is the glue you need to access prims outside the linkset. Once you get the id of an external object (maybe using llSensor), you may call llGetObjectPrimCount (also consider osGetPrimCount which does not count sitting avatars) then iterate on llGetObjectLinkKey to get the id of each prim in the remote object. Finally, apply osGet/SetPrimitiveParams.

The following tables tries to sort out the functions available to manipulate prims, for each addressing method.


Target is script's host
(prim containing the script)
Target is link number
(inside the linkset)
Target is prim id
(any prim you own in the scene)
llGetOwner
llGetKey llGetLinkKey(num)
llSetText
llGetRot / llSetRot
llGetScale / llSetScale
llGetAlpha / llSetAlpha llSetLinkAlpha(num)
llGetColor / llSetColor llSetLinkColor(num)
llGetPos / llSetPos
llGetObjectName / llSetObjectName llGetLinkName(num)
llGetObjectDesc / llSetObjectDesc
llGetNumberOfSides llGetLinkNumberOfSides(num)
llGetTexture / llSetTexture llSetLinkTexture(num)
llSetTextureAnim llSetLinkTextureAnim(num)
llParticleSystem llLinkParticleSystem(num)
llSetPrimMediaParams llSetLinkMedia(num)
llGetObjectPrimCount(uuid)
llGetObjectDetails(uuid)
llGetObjectLinkKey(uuid,num)
"Universal" access (list of rules)
llGet/SetPrimitiveParams llGet/SetLinkPrimitiveParams(num) osGet/SetPrimitiveParams(uuid)


Example : A replicator

Put this script in a box, along with a single-prim object named "seed". It will iterate on each prim of a named object you own in the scene, then rez an identical prim at a given offset, perfectly replicating the source object, even meshes. Sleeps are here for demo. Remove them for speedy replication. CLEANUP option will get rid of rezed prims.

string targetName = "Example"; // The name of an object you own in the scene
string seedName = "seed";      // Name of the seed prim 
vector offset = <0,0,1>;       // Offset of the dupicata
 
integer primcnt;
integer primnum;
key rootKey;
 
integer TupleLength (integer param)
{
    if (param == PRIM_GLOW)       return 1;
    if (param == PRIM_COLOR)      return 2;
    if (param == PRIM_TEXGEN)     return 1;
    if (param == PRIM_TEXTURE)    return 4;
    if (param == PRIM_NORMAL)     return 4;
    if (param == PRIM_SPECULAR)   return 7;
    if (param == PRIM_BUMP_SHINY) return 2;
    if (param == PRIM_FULLBRIGHT) return 1;
 
    return 0; // Not a by-side parameter
}
 
list IterateFaces (list p, integer param)
{
    integer tuplelength = TupleLength (param);
    integer numsides =  llGetListLength(p) / tuplelength;
 
    // Une seule face, retourner [ param, ALL_SIDES, p ]
    if (numsides == 1) return [ param, ALL_SIDES ] + p;
 
    list result = [ ];
    for (integer s=0; s<numsides; s++) {
        list tuple = llList2List (p, s*tuplelength, (s+1)*tuplelength-1);
        result += [ param, s ] + tuple;
    }  
    return result;
}
 
CopyParams (key srcprim, key dstprim, integer param) {
    if (TupleLength(param) == 0)
    {
        list p = osGetPrimitiveParams (srcprim, [ param ]);
        if (param == PRIM_POSITION) p = [ llList2Vector (p,0) + <5,0,0> ];
        osSetPrimitiveParams (dstprim, [ param ] + p);
    }
    else
    {
        list p = osGetPrimitiveParams (srcprim, [ param, ALL_SIDES ]);
        p = IterateFaces (p, param);
        osSetPrimitiveParams (dstprim, p);
    }
    llSleep (0.1);
}
 
RezAPrim ()
{
    if (++primnum > primcnt) llResetScript();
    llRezObject (seedName, llGetPos()+offset, ZERO_VECTOR, ZERO_ROTATION, 0);
}
 
PrimRezed (key dstkey)
{
    llOwnerSay ("Processing link "+(string)primnum+" of "+(string)primcnt);
    key srckey = llGetObjectLinkKey (rootKey, primnum);
    llSleep (0.1);
 
    CopyParams (srckey, dstkey, PRIM_POSITION);
    CopyParams (srckey, dstkey, PRIM_ROTATION);
    CopyParams (srckey, dstkey, PRIM_TYPE);
    CopyParams (srckey, dstkey, PRIM_SIZE);
    CopyParams (srckey, dstkey, PRIM_COLOR);
    CopyParams (srckey, dstkey, PRIM_TEXTURE);
    CopyParams (srckey, dstkey, PRIM_NORMAL);
    CopyParams (srckey, dstkey, PRIM_SPECULAR);
    CopyParams (srckey, dstkey, PRIM_TEXGEN);
    CopyParams (srckey, dstkey, PRIM_BUMP_SHINY);
    CopyParams (srckey, dstkey, PRIM_FULLBRIGHT);
    CopyParams (srckey, dstkey, PRIM_PHYSICS_SHAPE_TYPE);
 
    RezAPrim ();
}
 
 
key OwnerOf (key id)
{
    list l = llGetObjectDetails (id, [ OBJECT_OWNER ]);
    return llList2Key (l, 0);
}
 
 
default
{
    state_entry()
    {
        llSetText ("READY", <0,1,0>, 1.0);
    }
 
    touch_start (integer n)
    {
        state dialog;
    }
}
 
 
integer dlgchan = 99;
integer dlglistener;
state dialog
{
    state_entry()
    {
        llSetText ("LISTENING", <0,1,0>, 1.0);
        llDialog (llGetOwner(), "Action?", ["CLEANUP", "REPLICATE"], dlgchan);
        dlglistener = llListen (dlgchan, "", llGetOwner(), "");
        llSetTimerEvent (10);
    }
 
    listen (integer chan, string name, key id, string msg)
    {
        if (msg == "CLEANUP")   state cleanup;
        if (msg == "REPLICATE") state replicate;
    }
 
    timer ()
    {
        state default;
    }
 
    state_exit ()
    {
        llSetTimerEvent (0.0);
        llListenRemove (dlglistener);
    }
}
 
 
state cleanup
{
    state_entry()
    {
        llSetText ("CLEANUP", <0,1,0>, 1.0);
        llSensor (seedName, NULL_KEY, PASSIVE|SCRIPTED, 100, PI);
    }
 
    sensor (integer num) {
        llOwnerSay ("Garbage collecting "+(string)num);
        for (integer i=0; i<num; i++) osDie (llDetectedKey(i));
        state default;
    }
 
    no_sensor() {
        llOwnerSay ("All clean.");
        state default;
    }
}
 
 
state replicate
{
    state_entry()
    {
        llSetText ("REPLICATE", <0,1,0>, 1.0);
        llSensor (targetName, NULL_KEY, PASSIVE|SCRIPTED, 100, PI);
    }
 
    sensor (integer num) {
        rootKey = llDetectedKey (0);
 
        if (OwnerOf(rootKey) != llGetOwner())
        {
            llOwnerSay ("I don't own "+targetName+". Aborting.");
            state default;
        }
 
        primcnt = llGetObjectPrimCount (rootKey);
        primnum = 0; RezAPrim ();
    }
 
    no_sensor() {
        llOwnerSay ("Object "+targetName+" was not found.");
    }
 
    object_rez (key uuid) {
        PrimRezed (uuid);
    }
}

Writing accessors

The Get/SetPrimitiveParams family of functions is awesomely powerful. It is also quite arcane to write and decipher. A way to get rid of these long lists of parameters cluttering your code is to encapsulate Get/SetPrimitiveParams in individual get/set accessors, actually 'filling the blanks' in the above table. Although this is much pleasant to read, note that doing so may hurt performance when you want to change many properties at once, which SetPrimitiveParams can do in one call. The following library is offered as an example.

// Index of properties for PRIM_TYPE_BOX, PRIM_TYPE_CYLINDER, PRIM_TYPE_PRISM
 
integer INDEX_TYPE      = 0;
integer INDEX_HOLE      = 1;
integer INDEX_CUT       = 2;
integer INDEX_HOLLOW    = 3;
integer INDEX_TWIST     = 4;
integer INDEX_TAPER     = 5;
integer INDEX_SHEAR     = 6;
 
// Index of properties for PRIM_TEXTURE
 
integer INDEX_TEXTURE   = 0;
integer INDEX_REPEATS   = 1;
integer INDEX_OFFSETS   = 2;
integer INDEX_ROTATION  = 3;
 
 
//                client        script
//          +---------------+------------+
//  Taper   | -1.0 0  +1.0  |  2.0   0.0 | 
//  Shear   | -0.5 0  +0.5  | -0.5  +0.5 |
//  Twist   | -180 0  +180  | -1.0  +1.0 | 
//          +---------------+------------+
 
 
//
// Set accessors
//
 
mySetLinkName (integer link, string text) {
    llSetLinkPrimitiveParamsFast (link, [PRIM_NAME, text]);
}
 
mySetLinkDesc (integer link, string text) {
    llSetLinkPrimitiveParamsFast (link, [PRIM_DESC, text]);
}
 
mySetLinkText (integer link, string text, vector color) {
    llSetLinkPrimitiveParamsFast (link, [PRIM_TEXT, text, color, 1.0]);
}
 
mySetLinkColor (integer link, vector color, integer face) {
    llSetLinkPrimitiveParamsFast (link, [PRIM_COLOR, face, color, 1.0]);
}
 
mySetLinkBright (integer link, integer bright, integer face) {
    llSetLinkPrimitiveParamsFast (link, [PRIM_FULLBRIGHT, face, bright]);
}
 
mySetLinkGlow (integer link, float glow, integer face) {
    llSetLinkPrimitiveParamsFast (link, [PRIM_GLOW, face, glow]);
}
 
// Les 3 suivants ne marchent pas pour ALL_SIDES
 
mySetLinkTexture (integer link, key texture, integer face) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_TEXTURE, face]);
    l = llListReplaceList(l, [texture], INDEX_TEXTURE, INDEX_TEXTURE);
    llSetLinkPrimitiveParamsFast (link, [PRIM_TEXTURE, face] + l);
}
 
mySetLinkRepeat (integer link, vector repeat, integer face) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_TEXTURE, face]);
    l = llListReplaceList(l, [repeat], INDEX_REPEATS, INDEX_REPEATS);
    llSetLinkPrimitiveParamsFast (link, [PRIM_TEXTURE, face] + l);
}
 
mySetLinkOffset (integer link, vector offset, integer face) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_TEXTURE, face]);
    l = llListReplaceList(l, [offset], INDEX_OFFSETS, INDEX_OFFSETS);
    llSetLinkPrimitiveParamsFast (link, [PRIM_TEXTURE, face] + l);
}
 
mySetLinkPos (integer link, vector pos) {
    llSetLinkPrimitiveParamsFast (link, [PRIM_POS_LOCAL, pos]);
}
 
mySetLinkRot (integer link, rotation rot) {
    llSetLinkPrimitiveParamsFast (link, [PRIM_ROT_LOCAL, rot]);
}
 
mySetLinkSize (integer link, vector size) {
    llSetLinkPrimitiveParamsFast (link, [PRIM_SIZE, size]);
}
 
mySetLinkCut (integer link, vector cut) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_TYPE]);
    l = llListReplaceList(l, [cut], INDEX_CUT, INDEX_CUT);
    llSetLinkPrimitiveParamsFast (link, [PRIM_TYPE] + l);
}
 
mySetLinkTwist (integer link, vector twist) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_TYPE]);
    l = llListReplaceList(l, [twist], INDEX_TWIST, INDEX_TWIST);
    llSetLinkPrimitiveParamsFast (link, [PRIM_TYPE] + l);
}
 
mySetLinkTaper (integer link, vector taper) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_TYPE]);
    l = llListReplaceList(l, [taper], INDEX_TAPER, INDEX_TAPER);
    llSetLinkPrimitiveParamsFast (link, [PRIM_TYPE] + l);
}
 
mySetLinkShear (integer link, vector shear) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_TYPE]);
    l = llListReplaceList(l, [shear], INDEX_SHEAR, INDEX_SHEAR);
    llSetLinkPrimitiveParamsFast (link, [PRIM_TYPE] + l);
}
 
mySetLinkHollow (integer link, float hollow) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_TYPE]);
    l = llListReplaceList(l, [hollow], INDEX_HOLLOW, INDEX_HOLLOW);
    llSetLinkPrimitiveParamsFast (link, [PRIM_TYPE] + l);
}
 
//
// Get accessors
//
 
string myGetLinkName (integer link) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_NAME]);
    return llList2String (l,0);
}
 
string myGetLinkDesc (integer link) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_DESC]);
    return llList2String (l,0);
}
 
string myGetLinkText (integer link) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_TEXT]);
    return llList2String (l,0);
}
 
vector myGetLinkColor (integer link, integer face) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_COLOR, face]);
    return llList2Vector (l,0);
}
 
integer myGetLinkBright (integer link, integer face) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_FULLBRIGHT, face]);
    return llList2Integer (l,0);
}
 
float myGetLinkGlow (integer link, integer face) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_GLOW, face]);
    return llList2Float (l,0);
}
 
key myGetLinkTexture (integer link, integer face) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_TEXTURE, face]);
    return llList2Key(l,0);
}
 
vector myGetLinkRepeat (integer link, integer face) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_TEXTURE, face]);
    return llList2Vector(l,1);
}
 
vector myGetLinkOffset (integer link, integer face) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_TEXTURE, face]);
    return llList2Vector(l,2);
}
 
vector myGetLinkPos (integer link) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_POS_LOCAL]);
    return llList2Vector (l,0);
}
 
vector myGetLinkSize (integer link) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_SIZE]);
    return llList2Vector (l,0);
}
 
vector myGetLinkCut (integer link) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_TYPE]);
    return llList2Vector (l, INDEX_CUT);
}
 
vector myGetLinkTwist (integer link) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_TYPE]);
    return llList2Vector (l, INDEX_TWIST);
}
 
vector myGetLinkTaper (integer link) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_TYPE]);
    return llList2Vector (l, INDEX_TAPER);
}
 
vector myGetLinkShear (integer link) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_TYPE]);
    return llList2Vector (l, INDEX_SHEAR);
}
 
float myGetLinkHollow (integer link) {
    list l = llGetLinkPrimitiveParams (link, [PRIM_TYPE]);
    return llList2Float (l, INDEX_HOLLOW);
}
Personal tools
General
About This Wiki