Jump to content

Tutorial: An introduction to rezzing objects by script


Innula Zenovka
 Share

You are about to reply to a thread that has been inactive for 294 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

People sometimes ask about how to use llRezAtRoot and the related llRezObject, and particularly about how to position the rezzed object in relation to the rezzer, so that no matter how the rezzer is placed, the rezzed object always ends up in the same position, and looking in the same direction, relative to the rezzer, rather than, for example, always rezzing n metres north of the rezzer and looking south (which will probably place the object where you want it to be when the rezzer is rotated at <0,0,0> but not otherwise).   

Here I will demonstrate a simple tool to read the rezzed object's position and rotation in terms of the rezzer's frame of reference (which is what llRezAtRoot usually needs) and also show how to pass information from the rezzer to the rezzed object at run time.

I will also demonstrate a simple workaround for the sometimes annoying restriction that llRezAtRoot and llRezObject can rez articles no more than 10 metres away from the rezzer.

I hope this tutorial is helpful to anyone who wants to learn something about rezzing objects with LSL.    However, builders who have little or no scripting knowledge should find they can use  the tools and scripts here to make their own functioning rezzers without needing to do more than a bit of cutting and pasting, and basic editing. 

First, reading the rezzed object's position and rotation

Place the rezzer as you want it.   Then put this script in it:

//CALCULATE POSITION AND ROTATION FOR REZZED OBJECT, RELATIVE TO THE REZZER

integer iChannel = -39251389;//an arbitrary channel
key kOwner;
default {
    state_entry() {
        llListen(iChannel,"","","");
        kOwner = llGetOwner();
    }


    listen(integer channel, string name, key id, string message) {
        if(llGetOwnerKey(id) == kOwner){//if the object sending the message belongs to the owner of the object containing this script (the object)
            list temp = llGetObjectDetails(id, [OBJECT_POS,OBJECT_ROT]);//read its position and rotation on the region
            vector vMyPos = llGetPos();//read the position on the region of the object containing this script (the rezzer)
            vector vObjectPos = llList2Vector(temp,0);//pull out the object's region position
            rotation rMyRot = llGetRot();//read the rezzer's rotation on the region
            rotation rObjectRot = llList2Rot(temp,1);//pull out the object's rotation.
            vector vOffset = (vObjectPos/rMyRot) - (vMyPos/rMyRot); //calculate the object's offset from the rezzer, correcting for the rezzer's local rotation
            //this formula gives what the result would be if the rezzer is at ZERO_ROTATION and the object is in the same position relative to the rezzer 
            //that is, so many metres in front of the rezzer/behind it, to its left/to its right
            rObjectRot = rObjectRot/rMyRot;//
            //does a similar correction for the object's rotation, so it's "facing the rezzer/with its back to the rezzer/looking to the rezzer's left/looking to tje rezzer's right right "

            float f = llVecDist(vMyPos,vObjectPos);
            if(f>10.0){
                llOwnerSay("Warning: the object "+name+" is "+(string)f+" metres from the rezzer.   llRezAtRoot fails if the rez position is > 10,0 metres.  Consider rezzing the "+name+" at the rezzer position, sending it the final destination in chat and using llSetRegionPos to move it there");
            }
            llOwnerSay("[\""+name+"\","+(string)vOffset+","+(string)rObjectRot+"];");

        }
    }
}

Then position the object you want to rez, the "rezzed object" as I'm calling it, where you want it to appear when it's rezzed by the rezzer -- so n metres in front of the object,  and x metres to its left, -- and rotate the rezzed object so it's pointing the way you want it, again in relation to the rezzer (so, looking at the rezzer, or looking away from it, or whatever).

Please be aware that scripts can rez objects no more than 10 metres away from the rezzer.  If you try rez something further away than that, the rezzer will silently fail and nothing will happen.

There is a workaround -- simply rez the object at the same location as the rezzer (or perhaps 2 metres underneath it, so it’s hidden) and then, after it’s rezzed, have the rezzer use llRegionSayTo to tell it where to go.   The script above, CALCULATE POSITION AND ROTATION, will therefore calculate positions more than 10 metres away but will also warn you that it is too far to rez in one operation.

If you want the rezzed item to move to a position more than 10 metres from the rezzer, please pay particular attention to the final section of this post (which also demonstrates an alternative method for passing a channel to the rezzed object).

When you have positioned everything to your satisfaction, drop this very simple script into the rezzed object.

//TELL REZZER TO READ OBJECT POSITION AND ROTATION
//PUT ME IN THE OBJECT YOU ARE GOING TO REZ

integer iChannel = -39251389;

default {
	state_entry() {
		llRegionSay(iChannel,"boo!");//tell the rezzer object that the object to rez is in position
		llRemoveInventory(llGetScriptName());//that's all the script needs to do, so it will remove itself
	}
}

The rezzer will chat out a line of text, such as

["test rezzed object",<1.50000, 0.00000, 1.00000>,<0.00000, 0.00000, -1.00000, 0.00000>];

Make a note of it (including the square brackets and the final semi-colon)..  

You will see that the short script you just put in the “rezzed object” has vanished (or it will soon, if it still seems to be there).  It’s no longer needed.

At this point you can also, if you wish, remove the CALCULATE POSITION AND ROTATION FOR REZZED OBJECT script from the rezzer.

Second, installing the rezzer script…

Scripts, obviously, need events to trigger them to do something.   In these examples, the llRezAtRoot function is called in both the touch_end and collision_start events.    That’s really to demonstrate this -- you could also call llRezAtRoot in a sensor or listen event.   It does not, of course, mean that someone needs  both to touch the object AND collide with it to make it work -- either will trigger it.  

Place this script, REZ OBJECT BY SCRIPT, in the rezzer

//REZ OBJECT BY SCRIPT.   PUT ME IN THE REZZER

float fLifeSpan = 30.0;//how long the object lives for
integer iStartParam = 99;//arbitrary integer to feed rezzed object as its start parameter
key kObject;
list lParams;

rotation rRot;
rotation rObjectRot;
string strObject;
vector vPos ;
vector vOffset;

default {
	state_entry() {//some sanity checks
		if(lParams ==[]){
			llOwnerSay("Please fill in some values for lParams at the start of the script.");
		}
		else{
			strObject = llList2String(lParams,0);//read the object's name from the list lParams
			if(llGetInventoryType(strObject)!=INVENTORY_OBJECT){ //if it's not an item in my inventory, warn my owner
				llOwnerSay("There is nothing in my inventory for me to rez.");
			}
			else{
				vOffset = llList2Vector(lParams, 1);//read the object position
				if(llVecMag(vOffset)>10.0){//if the offset is greater than 10 metres
					llOwnerSay("The position at which "+strObject+" is to rez is more than 10 metres away from the rezzer position.  I can rez objects only within a 10 metre radius.  You will need to rez the "+strObject+" closer to me and then to use llSetRegionPos to move it where you want it to go.");
				}
			}



		}

	}


	changed(integer change){
		if(change & CHANGED_INVENTORY){
			llResetScript();//if the inventory contents change, check for what the object is the script  to rez.
		}
	}

	touch_end(integer num_detected) {
		if(!llGetObjectPrimCount(kObject)){//if there is no object already rezzed
			if(llGetInventoryType(strObject)==INVENTORY_OBJECT){//don't try to rez something that's not there
			vPos = llGetPos();//read the position of the object containing this script
			rRot = llGetRot();//read the location of the object containing this script
			vOffset = llList2Vector(lParams, 1);//read from lParams the offset from the rezzer at which the rezzed object should rez
			rObjectRot = llList2Rot(lParams,2);//read from lParams the rotation at which it is to rez
			llRezAtRoot(strObject, vPos + vOffset * rRot, ZERO_VECTOR, rObjectRot * rRot, iStartParam);
			}
		}
	}

	collision_start(integer num_detected) {
		if(!llGetObjectPrimCount(kObject) && (llDetectedType(0)& AGENT_BY_LEGACY_NAME)){//if there is no object already rezzed, and if it was an avatar who collided with the rezzer
			if(llGetInventoryType(strObject)==INVENTORY_OBJECT){//don't try to rez something that's not there
			llSleep(0.5);//wait a moment to give the avatar time to step onto the prim before proceeding
			vPos = llGetPos();//read the position of the object containing this script
			rRot = llGetRot();//read the location of the object containing this script
			vOffset = llList2Vector(lParams, 1);//read from lParams the offset from the rezzer at which the rezzed object should rez
			rObjectRot = llList2Rot(lParams,2);//read from lParams the rotation at which it is to rez
			llRezAtRoot(strObject, vPos + vOffset * rRot, ZERO_VECTOR, rObjectRot * rRot, iStartParam);
			}
		}
	}

	object_rez(key id) {
		llSleep(0.2);//brief delay to allow the rezzed object to wake up and for the simulator to register its UUID
		kObject = id; //record the object's id
		llRegionSayTo(kObject, iStartParam, (string)fLifeSpan);//tell the object how long to wait before deleting itself
	}
}

Find the entry list lParams; at the top of the script (line 3, or near there) and copy paste the line of text you saved earlier when the rezzer chatted it out) so that the line now reads, for example,

list lParams = ["test rezzed object",<1.50000, 0.00000, 1.00000>,<0.00000, 0.00000, -1.00000, 0.00000>];

What is this list for?    It holds some values that are needed by llRezAtRoot, the function that does the magic.   llRezAtRoot takes five arguments (that is, it needs five pieces of information to function), presented in a list:

llRezAtRoot([
     (string) Name of Object to rez,
     (vector) Initial Position,
     (vector) Initial Velocity,
     (rotation) Initial rotation,
     (integer) Start Parameter
]);

The first argument, the name of the object to rez, should be self-explanatory.

The second argument, Initial Position, is a vector containing the position on the region where the object is to appear.   Since you will normally want to be able to use the rezzer anywhere on a region, you need to express as rezzer position + offset.   

Furthermore, you will generally want to use the rezzer’s frame of reference rather than the region’s frame of reference -- that is, no matter which way the rezzer happens to be pointing, the object should rez 1.5 metres in front of the object (object’s frame of reference) regardless of whether that happens to be 1.5 metres north of the object or south, or north by north west of the object (region frame of reference).

To tell the script that’s what you want to happen, you have to tell it to translate the offset by the rezzer’s rotation, which is what

			vPos = llGetPos();//read the position of the object containing this script
			rRot = llGetRot();//read the location of the object containing this script
			vOffset = llList2Vector(lParams, 1);//read from lParams the offset from the rezzer at which the rezzed object should rez
			rObjectRot = llList2Rot(lParams,2);//read from lParams the rotation at which it is to rez
			llRezAtRoot(strObject, vPos + vOffset * rRot, ZERO_VECTOR, rObjectRot * rRot, iStartParam);

in the touch_end and collision_start events is all about.   vPos is the object’s position.  vOffset is the desired offset from the rezzer, assuming the rezzer is rotated at <0,0.0,> (facing due East in SL terms).   *rRot means “translate this position through my own frame of reference” or “so many metres in front of me, and so many metres to my left”.  

The asterisk may look as it it means “multiply by” but it doesn’t in this context.   It means translate.  The significant difference in this context is that multiplication is commutative (6 x 3 == 3 x 6) but translation isn’t ( vVector * rRotation means something very different from rRotation * vVector).

The third argument, Initial Velocity, isn’t relevant here.  It’s the speed and directon at which physical objects should be moving when they are rezzed (e.g. bullets rezzed by a gun).  Since it’s not relevant here, the value is ZERO_VECTOR.

The fourth argument, Initial Rotation, means the initial rotation of the rezzed object, and translating it by the rezzer’s rotation tells it use the rezzer’s frame of reference (“looking towards me” rather than “looking to my north”)..

The fifth and final argument, Start Parameter, is an arbitrary integer that you send to the rezzed object to use as its start parameter.   Typically, as in this example, it’s used to tell the rezzed object to open a listener on a particular channel, so the rezzer can  send the rezzed object further instructions after rezzing it.

Note that when either the touch_end or collision_start events fire, they check that

if(!llGetObjectPrimCount(kObject)){//if there is no object already rezzed

In English that means, “if the prim count (land impact) of the object with the UUID kObject is zero,”  which is a roundabout way of saying “if there is no object on the region with the UUID kObject.

If there is no object already rezzed, then the script goes ahead and rezzes the object at the required offset and rotation.   

It then, in the object_rez event, takes a moment to let things settle down after the object rezzes, then records its UUID (kObject = id;) and then uses llRegionSayTo to pass it the value of fLifeSpan (the number of seconds to wait before deleting itself).    It could just as well, or even also, send it a position to move to using llSetRegionPos, as suggest above.

Third and finally, the script in the rezzed object

Place the following script in the object you want to rez:

//SCRIPT FOR REZZED OBJECT -- PUT ME IN THE OBJECT YOU WANT TO REZ

float fHowLong;

integer iChannel;

default {
	on_rez(integer start_param) {
		if(start_param){//if the start parameter is not 0, that means the object containing this script came out of a rezzer, not someone's inventory
			iChannel = start_param; //use the start parameter as a channel
			llListen(iChannel,"","","");//start listening on that channel
		}
	}

	listen(integer channel, string name, key id, string message) {
		fHowLong = (float)message;
		llOwnerSay("setting timer for "+(string)fHowLong+" seconds.");//remove this after you've finished testing, obviously
		llSetTimerEvent(fHowLong);
	}


	timer() {
		llDie();//delete this object
	}


}

As noted above, this reads the value of start_param, the start parameter passed in the llRezAtRoot call, and opens a listener using that value as the channel.  The rezzer then sends it, using llRegionSayTo, the value of fLifeSpan, which it casts as a float and uses it to set a timer.   When the timer fires, the object vanishes.

If you want the object to remain rezzed indefinitely, then simply supply 0.0 as the value for fLifeSpan (llSetTimerEvent(0.0) means, possibly unintuitively, turn off the timer rather than make it fire immediately).

Workaround for llRezAtRoot’s 10 metre limit

As noted above, llRezAtRoot and llRezObject fail if they try to rez the object more than 10 metres away.   However, there is an easy workaround.   You simply need to make some slight alterations both the rezzer script and the script for the rezzed object.

Use this script in the rezzer.  It rezzes the object underneath it (so people do not see an object appear at the rezzer and then vanish) and then tells the object where to go on the region.

These two examples also demonstrate a way to have thescripts calculate a channel based on the rezzer’s UUID and an alternative way to pass the value of fLifeSpan.    If you don’t want the object to remove itself after a certain time, simply set fLifeSpan to 0.0 (though if you do this, remember that the script checks before rezzing anything and won’t rez a new object while the old one is still on the region).

//REZ OBJECT BY SCRIPT AND GIVE IT A DESTINATION ON THE REGION.   PUT ME IN THE REZZER
float fLifeSpan = 30.0;//how long the object lives for
integer iChannel;
key kObject;
list lParams;
rotation rRot;
rotation rObjectRot;
string strObject;
vector vPos ;
vector vOffset;

integer Key2Number(key objKey) {
  return ((integer)("0x"+llGetSubString((string)objKey,-8,-1)) & 0x3FFFFFFF) ^ 0x3FFFFFFF;
}
default {
	state_entry() {
		iChannel = Key2Number(llGetKey());//calculate an integer based on my UUID
		if(lParams ==[]){
			llOwnerSay("Please fill in some values for lParams at the start of the script.");
		}
		else{
			strObject = llList2String(lParams,0);//read the object's name from the list lParams
			if(llGetInventoryType(strObject)!=INVENTORY_OBJECT){ //if it's not an item in the inventory of the object containing this script, complain
				llOwnerSay("There is nothing in my inventory for me to rez.");
			}
		}

	}
	changed(integer change){
		if(change & CHANGED_INVENTORY){
			llResetScript();//if the inventory contents change, check for the object that is to be rezzed
		}
	}
	touch_end(integer num_detected) {
		if(!llGetObjectPrimCount(kObject)){//if there is no object already rezzed
			if(llGetInventoryType(strObject)==INVENTORY_OBJECT){//don't try to rez something that's not there
			vPos = llGetPos();//read the position of the object containing this script
			rRot = llGetRot();//read the location of the object containing this script
			vOffset = llList2Vector(lParams, 1);//read from lParams the offset from the rezzer at which the rezzed object should rez
			rObjectRot = llList2Rot(lParams,2);//read from lParams the rotation at which it is to rez
			llRezAtRoot(strObject, vPos +<0.0,0.0,-2.0>, ZERO_VECTOR, rObjectRot * rRot,(integer)fLifeSpan);//rez the object 2 metres beneath the rezzer, so people don't see it rez, and pass the lifespan as the start parameter
			}
		}
	}
	collision_start(integer num_detected) {
		if(!llGetObjectPrimCount(kObject) && (llDetectedType(0)& AGENT_BY_LEGACY_NAME)){//if there is no object already rezzed, and if it was an avatar who collided with the rezzer
			if(llGetInventoryType(strObject)==INVENTORY_OBJECT){//don't try to rez something that's not there
			llSleep(0.5);//wait a moment to give the avatar time to step onto the prim before proceeding
			vPos = llGetPos();//read the position of the object containing this script
			rRot = llGetRot();//read the location of the object containing this script
			vOffset = llList2Vector(lParams, 1);//read from lParams the offset from the rezzer at which the rezzed object should rez
			rObjectRot = llList2Rot(lParams,2);//read from lParams the rotation at which it is to rez
			llRezAtRoot(strObject, vPos +<0.0,0.0,-2.0>, ZERO_VECTOR, rObjectRot * rRot,(integer)fLifeSpan);//rez the object 2 metres beneath the rezzer, so people don't see it rez, and pass the lifespan as the start parameter
			}
		}
	}
	object_rez(key id) {
		llSleep(0.2);//brief delay to allow the rezzed object to wake up and for the simulator to register its UUID
		kObject = id; //record the object's id
		llRegionSayTo(kObject, iChannel, (string)(vPos + vOffset * rRot));//tell the object its target position
	}
}

Use this script in the rezzed object.   It listens for a message from the rezzer and, when it receives it, it moves to that position.   It initially sets itself to TEMP_ON_REZ so that, if anything goes wrong and it doesn’t receive any message giving it a destination, it will vanish in a minute or so.   If it receives an invalid destination, or if it cannot reach its destination, it deletes itself.

//MOVE ANYWHERE ON REGION -- PUT ME IN THE REZZED OBJECT
float fHowLong;

integer iHandle;

integer Key2Number(key objKey) {
  return ((integer)("0x"+llGetSubString((string)objKey,-8,-1)) & 0x3FFFFFFF) ^ 0x3FFFFFFF;
}

vector vTargetPos;

default {
	on_rez(integer start_param) {

			key kRezzer = llList2Key(llGetObjectDetails(llGetKey(), [OBJECT_REZZER_KEY]),0);//read the uuid of the person or object that rezzed the object containing this script
			if(llGetAgentSize(kRezzer)==ZERO_VECTOR){//if the agent who rezzed the object containing this script  doesn't have a size, it must have been rezzed by an object
				iHandle = llListen(Key2Number(kRezzer),"",kRezzer,"");//calculate the channel based on the rezzer's uuid and start listening
				llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_TEMP_ON_REZ,TRUE]);//set to temp on rez, in case something goes wrong and the object containing this script  can't reach its destination
				fHowLong = (float)start_param;//read value of fHowLong from the start_param.  A value of 0 means the object remains indefinitely, assuming it makes it to its ultimate destination
			}


	}

	listen(integer channel, string name, key id, string message) {
		llListenRemove(iHandle);//stop listening
		vTargetPos = (vector)message;
		if(vTargetPos !=ZERO_VECTOR){//there is probably an edge case where someone might actually want to place the object underground at the corner of the region, but usually it means the message isn't a valid vector
			llSetRegionPos(vTargetPos);
			llSleep(0.1);//probably not necessary but to be on the safe side, before the next test
			if(llVecDist(llGetPos(), vTargetPos)>0.25){//if the arrival point and the target point are close enough for government work....
				// in theory you could say "if(llSetRegionPos(vTarget)" but that will fail if, for some reason, the object is even fractionally target so it's safer to allow some leeway)
				llOwnerSay("I have failed to reach my target.  Oh, the shame and disappointment!  Farewell, cruel world!");
				llDie();
			}
			else{
				llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_TEMP_ON_REZ,FALSE]);//arrived safely, so turn off temp on rez
				llOwnerSay("setting timer for "+(string)fHowLong+" seconds.");//remove this after you've finished testing, obviously
				llSetTimerEvent(fHowLong);
			}

		}
		else if (ZERO_VECTOR == vTargetPos){
			llOwnerSay("ZERO_VECTOR  is not a valid destination for me.   Dying because I don't know what else to do.");
			llDie();
		}

	}


	timer() {
		llDie();//delete the object containing this script 
	}


}

 

Edited by Innula Zenovka
  • Like 5
  • Thanks 6
Link to comment
Share on other sites

IIRC, the 10 meter rez limit was changed around the time of the new prim accounting system in 2011. The limit is now based on the rezzing link's scale.

The formula for rez distance is 10.0 + ((triagonal of the rezzing link's scale) * 0.5).

So, a <1,1,1> sized rezzer link can rez 10.866 meters away, a <10,10,10> sized rezzer link can rez 18.66 meters away and so on.

Edited by Lucia Nightfire
  • Like 1
Link to comment
Share on other sites

Thanks.  I never use llRezObject, so I hadn't seen those notes.   Interesting.  

I see, though, that the notes warn, 

Quote

Setting a script up to rez objects outside the previously established known maximum of 10.0 meters is something that you need to be aware may stop working at any time in the future. Without solid verification from Linden Labs, 10.0 meters should be considered a safe maximum distance.

so I don't think I'll be using it without verification from LL.   It doesn't seem to do much that rezzing the object at or near the rezzer location and then giving it a destination or the uuuid of a target doesn't.   Or am I missing something?

Edited by Innula Zenovka
  • Like 1
Link to comment
Share on other sites

Speed and/or aesthetics are the only factors that come to mind.

Rezzing then positioning could be a few frames slower than rezzing at target.

Aside from weaponry, it might not be as critical, but there may be other unique/unorthodox use cases that might require it.

There is also the issue with possible unwanted interpolation effects. Something I wish we had to ability to dictate at the object level whether or not to allow interpolated positioning, rotating or scaling.

Link to comment
Share on other sites

It's worth knowing about, I agree.  But, speaking personally, I'd be nervous about using it unless we have verification from LL that it's supposed to work that way, since there's always the risk LL will one day break it otherwise.   I don't see that as a risk worth taking simply to make things a few frames quicker (particularly with weaponry, since during a combat simulation there are going to be so many factors slowing things down anyway that anything which depends on split-second timing will be problematic).

As to interpolation, my impression (though I've never really examined it in detail) is that rezzing the object out of sight (2 metres below the rezzer, in my example) and then calling llSetRegionPos makes the object suddenly pop into sight at the target position, since there's no visible start position from which to interpolate the movement.   But I could well be mistaken about that.

 

Link to comment
Share on other sites

not sure if this is relevant but...skips using a listen?

Rezzer:

default
{
     touch_start(integer param)
     {   vector MoveTo = <165,50,3024>; // either region pos or coords based on curr pos.
         llSetObjectDesc( (string)MoveTo );
         vector offset =  llGetPos() + <0.0,0.0,-2.0>; // 2 meters below rezzer
         llRezObject("child2",offset, <0.0,0.0,0.0>, <0.0,0.0,0.0,1.0>, 0);
     }
}

 

Rezzed:

default
{
   on_rez(integer start_param)
    {      
        key  id      = llGetKey();
        list details = llGetObjectDetails(id,
             [OBJECT_REZZER_KEY]);
        key rzr = llList2String(details, 0);
        list details2 = llGetObjectDetails(rzr,
              [OBJECT_DESC]);
        string loc =     llList2String(details2, 0)   ;
        llOwnerSay(" Moving to... : " +  loc );
        integer hasMoved = llSetRegionPos( (vector)loc );
          if (!hasMoved)
          llOwnerSay("Move was not possible!");
    }
}

 

Edited by Xiija
  • Like 5
Link to comment
Share on other sites

Another no-listener approach I've used for things like couple danceballs is to initially rez the object nearby, at a location matching the fractional components of the target location's x, y, and z dimensions, then pass the integer components compressed into a single integer as the rezzed object script's on_rez start_parameter. Within a region, x and y are limited to 255, so those should fit in 8-bits each, leaving 16 bits for the z (4096 is 2^13, so that seems plenty for most purposes).

  • Like 4
Link to comment
Share on other sites

Yet another no-listener approach, which I sometimes use when I want to rez something near an avatar (so it would work well with danceballs) is to use the Key2Number userfunction I mentioned in the OP to calculate an integer based on the UUID of the target avatar and pass it to the rezzed object as the start parameter.

Then, in the on_rez event in the rezzed object, I do something like 

integer Key2Number(key objKey) {
  return ((integer)("0x"+llGetSubString((string)objKey,-8,-1)) & 0x3FFFFFFF) ^ 0x3FFFFFFF;
}
key kTarget;
default
{

	on_rez(integer start_param)
	{
		if(start_param){
			list temp = llGetAgentList(AGENT_LIST_PARCEL,[]);//get a list of avatars on the parcel
			//I use this rather than a sensor partly because it's simpler but also because llSensor returns only 16 hits, and on a crowded parcel things can go wrong very easily
			integer max = -llGetListLength(temp);
			do {//loop through the list of avatars on the parcel
				key k = llList2Key(temp, max);
				if(Key2Number(k)==start_param){//identified the target avatar
					kTarget = k;
					max = -1;//stop looking
				}
			}
			while (++max);
			if(llGetAgentSize(kTarget)){//if there is a target
				//do stuff, like grab the target's position and rotation and move to an offset of that
			}
			else{//no target
				llOwnerSay("Can't find a target");
				llDie();//not much else to do here
			}
		}
	}
}

 

Edited by Innula Zenovka
  • Like 2
Link to comment
Share on other sites

Before OBJECT_REZZER_KEY was released on agni I tested my interceptor shield's ability to pass a dozen appearance and operational data point indexes through the desc using a means similar to Xiija's and it works as fast as it did originally using the start param. It may cost a frame querying and reading llGetObjectDetails() but as far as the naked eye was concerned I saw no significant delay. It just means I can add more parameters with less compression in the future, possibly in csv format as well.

  • Like 1
Link to comment
Share on other sites

On 1/20/2018 at 6:20 AM, Innula Zenovka said:

Place the rezzer as you want it.

Thank you for this tutorial. However, 1. most of the time the very reason why I wanna use scripts to rez objects is because I am floating in the air with nothing around me (no floors, for example) as an anchor. 2. I do not wanna put the object in something else (the rezzer) before I can rez it. So what I want is some script that can

1. directly rez an object from my inventory, not from a rezzer, and with object name (or UUID) NOT predetermined in the script. 

2. can rez the object within a relative position in reference to me (the avatar), not in reference of a rezzer which need to be rezzed first.

I know that I can wear a rezzer as an attachment to solve the 2nd problem. However it is not ideal since I do want to get rid of the rezzer.

Please let me know if this is possible. Thanks.

Link to comment
Share on other sites

No, there's no way a script can access your inventory, at least not with LSL.   If you're using RLV, then there are RLV commands that can access your inventory to the extent that they can make you wear inventory items you've previously placed in a special folder, but even RLV can't rez items from your inventory.    

Sorry, but what you ask for can't be done with LSL.

As to rezzing things relative to you, that's simple enough.  I gave the formula in the post, but here it is again:

	list lDetails = llGetObjectDetails(lGetOwner(),[OBJECT_POS,OBJECT_ROT]);
		vector vPos = llList2Vector(lDetails,0);
		rotation rRot = llList2Rot(lDetails,1);
		rotation r180 = llEuler2Rot(<0.0.0.0,180.0> * DEG_TO_RAD);
		llRezAtRoot("My object", vPos + <1.0,0.0,0.0>*rRot,ZERO_VECTOR,r180 * rRot,99);//rez My Object 1 metre in front of the direction I'm looking in, rotated to face me (IE, through 180 degrees)with a start parameter of 99 (artibrary choice)..

 

 

  • Like 1
Link to comment
Share on other sites

12 hours ago, JessieHoo said:

Thank you for this tutorial. However, 1. most of the time the very reason why I wanna use scripts to rez objects is because I am floating in the air with nothing around me (no floors, for example) as an anchor. 2. I do not wanna put the object in something else (the rezzer) before I can rez it. So what I want is some script that can

1. directly rez an object from my inventory, not from a rezzer, and with object name (or UUID) NOT predetermined in the script. 

2. can rez the object within a relative position in reference to me (the avatar), not in reference of a rezzer which need to be rezzed first.

I know that I can wear a rezzer as an attachment to solve the 2nd problem. However it is not ideal since I do want to get rid of the rezzer.

Please let me know if this is possible. Thanks.

To add to what Innula said, certain third-party viewers (Like Firestorm) have chat commands that perform special actions.

Specifically, typing "rezplat" into local chat will rez a platform 2 meters below your avatar's feet. (No message will be shown in chat.)
By default the platform is 5x5x0, but you can specify any size between 0 and 64: "rezplat 1.5" = 1.5 x 1.5 x 0.01 platform

You can find all of the special Firestorm chat commands in Preferences > Chat > CmdLine

Edited by Wulfie Reanimator
  • Thanks 1
Link to comment
Share on other sites

On 12/14/2018 at 9:24 PM, JessieHoo said:

I am floating in the air with nothing around me (no floors, for example) as an anchor

a way around this is to make a prim platform say 10x10x0.5 or whichever. Save it to your inventory

when you are up in the sky then attach the platform to your Avatar Center. Then right-click on the platform which you are now wearing, and Drop

the platform will drop off your avatar and be rezzed inworld above your head. You now have a base prim platform to rez stuff on from Inventory 

  • Like 1
Link to comment
Share on other sites

On 12/15/2018 at 5:08 AM, Wulfie Reanimator said:

You can find all of the special Firestorm chat commands in Preferences > Chat > CmdLine

This is very helpful! Not only for the rezzing floor command but also for other commands. Thank you very much! I can always learn something when I post in this forum. 

Link to comment
Share on other sites

  • 5 months later...

Hello Innula, 

thank you for this amazing tutorial, I need ask you more. Im building planes, now the problem is get a script for ladder inside inventory plane and rez by chat followng plane rotation. Can you help me please?

Link to comment
Share on other sites

  • 4 months later...

Thank you so much for this tutorial, I was at a total loss of this type of use for scripts and this was the closest I found to understand them for an item I'm making for a friend. As I was reading some other threads on this subject I realized that I in fact needed to calculate avatar size first (kids and grownups might want to use this in future) . But I'm unsure how to use what I learned here... with the  (suggested from another forum here)  "size of avi - z of target rez position". Assuming from further searches I would need llGetAgentSize?  

My project is a hand worn item that when clicked rezzes an object in front of the avi  at feet level. Using the tutorial it works wonderfully! But when I use a grown up avi, well, of course it is hovering up a bit. I thought about just making a kid version, female height version and male height version. But, would love to learn how to use the avi size in your rezzer for future products and well, just to have that knowledge. :)

Anyway, if you would be so kind, or rather have the time, to show how to do that within your script it would be greatly appreciated and I would gladly pay you for your time if needed.  

I'm not posting the script because it's already here ;) *chuckles* I haven't changed a thing other than the lParams. 

Thanks in advance for any help. 

Link to comment
Share on other sites

35 minutes ago, MzPenny said:

As I was reading some other threads on this subject I realized that I in fact needed to calculate avatar size first (kids and grownups might want to use this in future) . But I'm unsure how to use what I learned here... with the  (suggested from another forum here)  "size of avi - z of target rez position". Assuming from further searches I would need llGetAgentSize?

It's slightly complicated if you can't visualize the problem in your head.

  • llGetPos will give you the position of your avatar's center.
    • To get to the floor level, you need to get the avatar's height (llGetAgentSize) and divide its height (the Z component) by half.
      (Because the distance from the center of your avatar to the floor is half of the avatar's height.)
       
  • llRezObject will rez an object so that its root's center will be at the given position.
    • If the root is tall, you will need to -- again -- add half of the root's height to the position you're about to give to llRezObject.
vector size = llGetAgentSize(llGetOwner());

float floor_distance = size.z / 2;

 

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

Thanks Wulfie, after grabbing some food and resting my eyes from all the previous reading *chuckles* I'm going to attempt to augment the tutorial script with your suggestions (using llGet Pos and llRezObject ) --- thank you so much for a direction to go in/try :)

 

Link to comment
Share on other sites

After doing some clean up of tutorial script, to keep just what I needed in order to understand all this newness without wading through other things... and it still worked so I didn't' break it. lol 
I went to reading and rereading this forum and making LOTS of notes from this forum .. smh.
As well as reading Wulfies comment a few times to wrap my brain around that.

Then, I assumed that... to make my rezzed object always land at the floor/feet (ground or air) regardless of avi size.. was to

To add into state_entry
vector size = llGetAgentSize(llGetOwner());
float floor_distance = size.z / 2;

Then to use 'floor_distance' as a replacement for the z of my rezzed object in this list from tutorial:
list lParams =["name of object",<1.59395, 0.05095, floor_distance>,<0.51440, 0.48518, -0.48518, -0.51440>];

It wasn't really a happy script until I put in a global of ----------- float floor_distance;

But after that she compiled and worked... BUT... well...
Good news, I didn't break anything.. Bad news.. it's rezzing like it's the center of the avatar thus not doing the math.

What am I missing? 
NOTE: my bounding box is very thin, so I assume it's not the objects center that's the problem?

Thanks for any help. 

Link to comment
Share on other sites

1 hour ago, MzPenny said:

After doing some clean up of tutorial script, to keep just what I needed in order to understand all this newness without wading through other things... and it still worked so I didn't' break it. lol 
I went to reading and rereading this forum and making LOTS of notes from this forum .. smh.
As well as reading Wulfies comment a few times to wrap my brain around that.

It wasn't really a happy script until I put in a global of ----------- float floor_distance;

But after that she compiled and worked... BUT... well...
Good news, I didn't break anything.. Bad news.. it's rezzing like it's the center of the avatar thus not doing the math.

What am I missing? 

I'm redacting my quotes a lot to not make my posts 1000000000000000 lines long. That said, I couldn't say what's wrong without seeing the full script. Admittedly I haven't read the original script you're trying to adapt (and I'm not going to), but anyway. Your originally said this:

8 hours ago, MzPenny said:

My project is a hand worn item that when clicked rezzes an object in front of the avi  at feet level.

So, you have an attachment with a touch_start event. In that event, you want to rez an object in front of the avatar, at ground level. Here's a more complete example:

float floor_distance;

default
{
    state_entry()
    {
        vector size = llGetAgentSize(llGetOwner());
        floor_distance = size.z / 2;
    }

    touch_start(integer n)
    {
        // Because you want to rez something based on the avatar's position
        // you will need llGetPos. Then you just need to add the distance
        // for where the object should be placed.
        vector position = llGetPos() + <1, 0, 0>;

        // That would rez the object directly in front of you.
        // To use the calculated floor distance, you must include it with
        // the minus-sign (because otherwise the positive value would cause
        // the object to be rezzed at head-level instead) like this:
        position = llGetPos() + <1, 0, -floor_distance>;

        // Lastly, you need to take the avatar's actual rotation into account
        // for the offset, so that the object will be in front of you no matter
        // which direction you face. Otherwise the object would always be
        // rezzed to the East because the positive X-axis points towards
        // East in SL's coordinates.
        position = llGetPos() + (<1, 0, -floor_distance> * llGetRot());

        // (You only really need the third example, not the two above it.)
        // vector position = llGetPos() + <1, 0, -floor_distance> * llGetRot();

        llRezObject("Object", position, ZERO_VECTOR, ZERO_ROTATION, 0);
    }
}

I recommend that you try removing some of the lines to see how it changes the outcome, and play around with it. Try changing it so that the object gets rezzed high on your right side instead. You should eventually also read the wiki page on applying rotations, but for now it's not necessary.

You can delete all the comments as well when you're done. (It does not set the object's rotation either, so I'll leave that up to you, or you can take what you learn from this to fix the script you already have.)

Edited by Wulfie Reanimator
Link to comment
Share on other sites

thank you wolfie! About to go to bed now, but will work with this new info tomorrow 1st thing. FYI: The referred to script is the one that started this thread/tutorial at the top, teaching how to use llRezAtRoot. ;) But like you said, no need, I 'think' I can study yours from a quick glance over to make it work with the floor_distance. We'll see right? *chuckles* Anyway, thank you so much for your reply and explanations with examples (which are always a good thing! lol) . I would not have known to use the minus sign for sure! Wish me luck! Sweet Dreams when they come to you. :) 

 

UPDATE! 

I did it!!! *does a happy dance* Well, YA'LL did it I just worked on getting it to work for my needs. *chuckles*  

A HUGE thank you to Innula and Wulfie for teaching this! *Nana hugs to both of you!*

Posting it just in case you see something that might cause issues in the future. ;) *chuckles*

Showing my paired down notes so I remember this in the future. lol 

-------------------------------------------------------------------------------------------------------------------------------------------

//got these numbers using forum method for lparm rezatroot but they worked!
//sets rotation of rezzed prim (NOT height/z )
rotation relativeRot = <0.51440, 0.48518, -0.48518, -0.51440>;

float floor_distance; //global used for determining individual avatar height

default
{

    touch_start(integer n)
    {
        //calculate for individual avatar size 
        vector size = llGetAgentSize(llGetOwner());
        floor_distance = size.z / 2;
        
        string Object= llGetInventoryName(INVENTORY_OBJECT,0);
        rotation myRot = llGetRot();
        
        //position is where it rezzes (left,right,height)
        vector position = llGetPos() + (<2, 0.0, -floor_distance> * myRot);
        
        //rotation is how the prims rotation is set relative to avi & rezzer ? 
        rotation rezRot = relativeRot * myRot;
        
        llRezObject(Object, position, ZERO_VECTOR, rezRot, 0);
    }
}

 

Edited by MzPenny
Link to comment
Share on other sites

  • 3 months later...
You are about to reply to a thread that has been inactive for 294 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...