Table of Contents

Shortnote

Body parts in Second Life that are made from sculpted primitives frequently consist of complementary sculpt maps. That is, a left-foot sculpt object is equivalent to a right-foot sculpte object where all sculptures have the Mirror option toggled.

The code below is a script that creates the complementary object. So for example, using this script you can create the left foot from the right foot, depending on how the sculpt maps were intended to be used.

Overview

Procedure

  1. Make a backup of all the objects you are going to use. This set-up procedure assumes that sculpted feet are created.
  2. Rez the left foot on the ground and place the Object Mirror script below in the object.
    1. Touch the foot and select Confirm to mirror and break all the links.
  3. Take all the objects that you unlinked in step ($2$), one by one, to your inventory.
  4. Rez an original copy of the left foot on the ground and in its inventory place all the objects from step ($3$).
  5. Place the Object Cloner script in the left foot from step ($3$) along with the objects from step ($3$).
  6. Touch the foot containing the script and the objects and follow the menus to clone the object on either axes.

Code: Object Mirror

object_mirror.lsl
//////////////////////////////////////////////////////////
// (C) Wizardry and Steamworks - 2011, License: GPLv3   //
//                                                      //
//               -* OBJECT MIRROR *-                    //
//                                                      //
//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
// About: Mirrors all the sculpts in a link-set.        //
//////////////////////////////////////////////////////////
// Usage: Make sure you CREATE a backup first since it  //
// will be needed later on by the OBJECT CLONER. Then   //
// drop this script in an object you would like to      //
// mirror. After that, touch the script and wait till   //
// all the sculpts are mirrored. When the process ends  //
// the object will un-link and the script will die.     //
//////////////////////////////////////////////////////////
 
 
default
{
    touch_start(integer num) {
        integer comChannel = ((integer)("0x"+llGetSubString((string)llDetectedKey(0),-8,-1)) & 0x3FFFFFFF) ^ 0xBFFFFFFF;
        llListen(comChannel, "", llDetectedKey(0), "");
        llDialog(llDetectedKey(0), "MIRROR: The following script will mirror all the sculpt maps in an object after which it will un-link the object and the script will die.Please confirm that you wish to proceed with the operations.", [ "[ Confirm ]", "[ Reject ]" ], comChannel);
    }
    listen(integer channel, string name, key id, string message) {
        if(message == "[ Confirm ]") {
            state rename;
        }
    }
}
 
state rename
{
    state_entry() {
        integer itra=llGetNumberOfPrims();
        do {
            llSetLinkPrimitiveParamsFast(itra, [PRIM_NAME, (string)itra]);
        } while(--itra>0);
        state mirror;
    }
}
 
state mirror
{
    state_entry() {
        integer itra=llGetNumberOfPrims();
        do {
            list lType = llGetLinkPrimitiveParams(itra, [PRIM_TYPE]);
            if(llList2Integer(lType, 0) == PRIM_TYPE_SCULPT) {
                llSetLinkPrimitiveParamsFast(itra, [PRIM_TYPE, llList2Integer(lType, 0), llList2Key(lType, 1), llList2Integer(lType, 2) | PRIM_SCULPT_FLAG_MIRROR]);
            }
        } while(--itra>0);
        state modify;
    }
}
 
state modify {
    state_entry() {
        llOwnerSay("MIRROR: Job done... Please make any changes to the primitives and then touch me again to unlink the object.");
    }
    touch_start(integer num) {
        llRequestPermissions(llGetOwner(), PERMISSION_CHANGE_LINKS);
    }
    run_time_permissions(integer perm) {
        if(perm & PERMISSION_CHANGE_LINKS) {
            state unlink;
        }
    }
}
 
state unlink {
    state_entry() {
        integer itra=llGetNumberOfPrims();
        do {
            llBreakLink(itra);
        } while(--itra>0);
        llOwnerSay("MIRROR: Please pick-up your objects one by one and refer to the cloner script, kthxbye!");
        llRemoveInventory(llGetScriptName());       
    }
}

Code: Object Cloner

object_cloner.lsl
//////////////////////////////////////////////////////////
// (C) Wizardry and Steamworks - 2011, License: GPLv3   //
//                                                      //
//               -* OBJECT CLONER *-                    //
//                                                      //
//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
// About: Mirrors all the sculpts in a link-set.        //
//////////////////////////////////////////////////////////
// Usage: Once you have picked up all the objects from  //
// the object crushed by OBJECT MIRROR, rez the linked  //
// object again and gently drop all the little pieces   //
// inside the new object. After that, add this script   //
// and touch it once to use the wizard.                 //
//////////////////////////////////////////////////////////
 
 
default {
    state_entry() {
        state rename;
    }
}
 
state rename {
    state_entry() {
        integer itra=llGetNumberOfPrims();
        do {
            llSetLinkPrimitiveParamsFast(itra, [PRIM_NAME, (string)itra]);
        } while(--itra>0);
        state axes;
    }
}
 
state axes {
    touch_start(integer num) {
        integer comChannel = ((integer)("0x"+llGetSubString((string)llDetectedKey(0),-8,-1)) & 0x3FFFFFFF) ^ 0xBFFFFFFF;
        llListen(comChannel, "", llDetectedKey(0), "");
        llDialog(llDetectedKey(0), "CLONER: Suppose that the object O represent the object, please select the most sensible axis to on which the object will be cloned. You can see the axes by right clicking the object and selecting edit.", [ "[ X ]", "[ Y ]", "[ Z ]" ], comChannel);
    }
    listen(integer channel, string name, key id, string message) {
        if(message == "[ X ]") {
            state x;
        }
        if(message == "[ Y ]") {
            state y;
        }
        if(message == "[ Z ]") {
            state z;
        }
    }
}
 
state x {
    state_entry() {
        integer itra=llGetNumberOfPrims();
        do {
 
            list scratch = llParseString2List(llList2String(llGetLinkPrimitiveParams(itra, [PRIM_POSITION]), 0), ["<", ",", ">"], []);
            vector vPos = ZERO_VECTOR;
            vPos.x = llList2Float(scratch, 0);
            vPos.y = llList2Float(scratch, 1);
            vPos.z = llList2Float(scratch, 2);
 
            scratch = llParseString2List(llList2String(llGetLinkPrimitiveParams(itra, [PRIM_ROTATION]), 0), ["<", ",", ">"], []);
            rotation vRot = ZERO_ROTATION;
            vRot.x = llList2Float(scratch, 0);
            vRot.y = llList2Float(scratch, 1);
            vRot.z = llList2Float(scratch, 2);
            vRot.s = llList2Float(scratch, 3);
 
            vector rPos = llGetRootPosition();
            rPos.x += 1;
            float reflectX = rPos.x;
            float diffRefl = vPos.x - reflectX;
 
            llRezObject(llList2String(llGetLinkPrimitiveParams(itra, [PRIM_NAME]), 0), <reflectX - diffRefl, vPos.y, vPos.z>, ZERO_VECTOR, vRot, 0);
        } while(--itra>0);
        state complete;
    }
}
 
 
state y {
    state_entry() {
        integer itra=llGetNumberOfPrims();
        do {
            list scratch = llParseString2List(llList2String(llGetLinkPrimitiveParams(itra, [PRIM_POSITION]), 0), ["<", ",", ">"], []);
            vector vPos = ZERO_VECTOR;
            vPos.x = llList2Float(scratch, 0);
            vPos.y = llList2Float(scratch, 1);
            vPos.z = llList2Float(scratch, 2);
 
            scratch = llParseString2List(llList2String(llGetLinkPrimitiveParams(itra, [PRIM_ROTATION]), 0), ["<", ",", ">"], []);
            rotation vRot = ZERO_ROTATION;
            vRot.x = llList2Float(scratch, 0);
            vRot.y = llList2Float(scratch, 1);
            vRot.z = llList2Float(scratch, 2);
            vRot.s = llList2Float(scratch, 3);
 
            vector rPos = llGetRootPosition();
            rPos.y += 1;
            float reflectY = rPos.y;
            float diffRefl = vPos.y - reflectY;
 
            llRezObject(llList2String(llGetLinkPrimitiveParams(itra, [PRIM_NAME]), 0), <vPos.x, reflectY - diffRefl, vPos.z>, ZERO_VECTOR, vRot, 0);
        } while(--itra>0);
        state complete;
    }
}
 
state z {
    state_entry() {
        integer itra=llGetNumberOfPrims();
        do {
            list scratch = llParseString2List(llList2String(llGetLinkPrimitiveParams(itra, [PRIM_POSITION]), 0), ["<", ",", ">"], []);
            vector vPos = ZERO_VECTOR;
            vPos.x = llList2Float(scratch, 0);
            vPos.y = llList2Float(scratch, 1);
            vPos.z = llList2Float(scratch, 2);
 
            scratch = llParseString2List(llList2String(llGetLinkPrimitiveParams(itra, [PRIM_ROTATION]), 0), ["<", ",", ">"], []);
            rotation vRot = ZERO_ROTATION;
            vRot.x = llList2Float(scratch, 0);
            vRot.y = llList2Float(scratch, 1);
            vRot.z = llList2Float(scratch, 2);
            vRot.s = llList2Float(scratch, 3);
 
            vector rPos = llGetRootPosition();
            rPos.z += 1;
            float reflectZ = rPos.z;
            float diffRefl = vPos.z - reflectZ;
 
            llRezObject(llList2String(llGetLinkPrimitiveParams(itra, [PRIM_NAME]), 0), <vPos.x, vPos.y, reflectZ - diffRefl>, ZERO_VECTOR, vRot, 0);
        } while(--itra>0);
        state complete;
    }
}
 
state complete {
    state_entry() {
        llRemoveInventory(llGetScriptName());
        llOwnerSay("MIRROR: Job done. Kthxbye!");
    }
}