Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

off-center rotations and relative positioning

Psistorm Ikura
Registered User
Join date: 19 Jul 2006
Posts: 52
01-28-2009 15:48
Ive recently run into a hugely difficult problem, while working on some prim animation scripts.
they store their position and rotation relative to the root prim (obtained via llGetLocalRot() and llGetLocalPos(), the actual data is written into a list before the script is compiled, so it wont get lost on resets)

now what I want to be able to, is this:
when two of those animated linksets are linked together, one linkset will "lose" its root prim. this would normally cause both linksets to occupy the same position. thus, I wanted to have the "lost" root prim to calculate its rotation/position offset from the new root, and tell those offsets to all the former child prims linked to it (the child prims can figure out which root is sending the offsets, they filter by its key, which was stored before any linking occured). that way, in theory, the prims should still move/rotate correctly in relation to the former root prim.

the problem:
I cant get them to. Ive looked at the wiki, and studied rotating vectors, but all I usually end up with is a rather bad mess. Im after a way to add offsets into the calculation, that will align a prim according to another prim in the linkset (the former root), instead of the actual root prim. if anyone can help me with this feat, itd be greatly appreciated!
Ruthven Willenov
Darkness in your light
Join date: 16 Jan 2008
Posts: 965
01-28-2009 16:20
From: Psistorm Ikura
Ive recently run into a hugely difficult problem, while working on some prim animation scripts.
they store their position and rotation relative to the root prim (obtained via llGetLocalRot() and llGetLocalPos(), the actual data is written into a list before the script is compiled, so it wont get lost on resets)

now what I want to be able to, is this:
when two of those animated linksets are linked together, one linkset will "lose" its root prim. this would normally cause both linksets to occupy the same position. thus, I wanted to have the "lost" root prim to calculate its rotation/position offset from the new root, and tell those offsets to all the former child prims linked to it (the child prims can figure out which root is sending the offsets, they filter by its key, which was stored before any linking occured). that way, in theory, the prims should still move/rotate correctly in relation to the former root prim.

the problem:
I cant get them to. Ive looked at the wiki, and studied rotating vectors, but all I usually end up with is a rather bad mess. Im after a way to add offsets into the calculation, that will align a prim according to another prim in the linkset (the former root), instead of the actual root prim. if anyone can help me with this feat, itd be greatly appreciated!


i assume you're using llSetPos and llSetLocalRot to move the prims in the animations?

that would be accord to the actual root, not the old root.

you could though use llGetObectDetails

list details = llGetObjectDetails(oldrootkey,[OBJECT_POS,OBJECT_ROT]);

vector oldrootpos = llList2Vector(details,0);
rotation olrdrootrot = llList2Rot(details,1);

llSetPos((oldrootpos+offset)*oldrootrot);
llSetRot(rotoffset+oldrootrot);

i'm sure it's not quite right, but hopefully you get the idea.

keep in mind though, that object keys change each time they're rezzed. not sure if it's true for no-copy items though.

i'm not sure if the rotation part is right, hopefully someone better skilled will come along and correct it if it's not
Psistorm Ikura
Registered User
Join date: 19 Jul 2006
Posts: 52
01-28-2009 17:20
its pretty close, ive been there myself. the poblem is however, that moving each prim by the root-to-root distance and then rotating isnt enough, they also need to move on an arc around the old root. now this is possible, but I cant figure out just how.

as for the changing object keys, this is known, and Ive coded it so that the prims always know the correct key :) - thanks for the reply btw
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
01-28-2009 19:52
This SHOULD do it. (Not yet compiled; may need some syntax fixes.)

CODE

setPosRotRelativeTo(integer virtualRootPrimNum, vector relPos, rotation relRot)
{
// ASSUMPTION: llGetObjectDetails() always succeeds for a prim in the same
// link set.
key virtualRootPrim = llGetLinkKey(virtualRootPrimNum);
list virtualRootDetails =
llGetObjectDetails(virtualRootPrim, [ OBJECT_POS, OBJECT_ROT ]);
vector virtualRootPos = llList2Vector(virtualRootDetails, 0);
rotation virtualRootRot = llList2Rot(virtualRootDetails, 1);

vector realRootPos = llGetRootPosition();
rotation realRootRot = llGetRootRotation();
vector virtualRootLocalPos = (virtualRootPos-realRootPos)/realRootRot;
rotation virtualRootLocalRot = virtualRootRot/realRootRot;

vector newLocalPos = (virtualRootLocalPos+relPos)*virtualRootLocalRot;
rotation newLocalRot = relRot*virtualRootLocalRot;

// NOTE: If you want to do this in one call set llSetPrimitiveParams(),
// you'll have to do a little extra work to account for SVC-93.
// See http://jira.secondlife.com/browse/SVC-93
llSetPos(newLocalPos);
llSetLocalRot(newLocalRot);
}

Psistorm Ikura
Registered User
Join date: 19 Jul 2006
Posts: 52
01-29-2009 05:41
thanks, Ill look at that right away :)
Im getting closer to it, still fighting inaccuracies though. I forgot to mention that these linksets will be attacheable, so I need to work fully in local space, but I think your examples should give me some fresh ideas to think about. right now, its perfect when I just apply a move offset, so Im pretty sure the part where I factor in rotations isnt perfect yet, since it has small but very noticeable inaccuracies still

edit:
actually, I should be able to circumvent those problems by grabbing the root rotation and position when the linksets are linked, since afterwards they arent supposed to be moved anymore anyways. this should prevent issues that occur using llGetRootRotation() in attached linksets, and work, since the root prims wont move in relation to eachother, they are static

edit2:
ok, Ive tested this, and found that it doesnt work out well at all. so in the end, I need to work completely on local coordinates. the solution you posted works for world coordinates, but wont be reliable for attachments, sadly, since llGetRootRotation() doesnt work reliably when the linkset is attached. what I feel needs to be calculated it the rotation/position offset of the old root towards the new, and I *think* the formula should look somewhat like that:
newPos + posOffset + ((posOffset - posOffset * rotOffset) * (newRot / llgetRootRotation()))

the problem is to adapt this formula to work without rootrotation and inside llSetPrimitiveParams(), because the way my product works, I really want to avoid making two calls for position/rotation, and handle scale and texture via primitiveParams. three calls are too long, id hugely prefer them working in one call
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
01-29-2009 11:33
Ah darn. Attachment. Yeah. In that case I believe you are going to need coordinated scripts in the virtual root prim and the moving prim(s). Something like (again not compiled; may need filxes):

Put the following script in each prim that is going to act like a virtual root prim to others.

CODE

//// Local Position Synch Header

// string arg is: <newLocalPos>,<newLocalRot>
integer LOCAL_POS_CHANGED_LINK = -386335658;


//// Constants

float DIST_TOLERANCE = 0.001;
float ANGULAR_TOLERANCE = 0.01;
float CHECK_PERIOD = 3.0;


//// Variables

vector currPos;
rotation currRot;


//// Functions

sendPosUpdate()
{
llMessageLinked(
LINK_ALL_OTHERS,
LOCAL_POS_CHANGED_LINK,
llList2CSV([ currPos, currRot ]),
NULL_KEY);
}


//// States

default
{
state_entry()
{
currPos = llGetLocalPos();
currRot = llGetLocalRot();
sendPosUpdate();

llSetTimerEvent(CHECK_PERIOD);
}

timer()
{
vector newPos = llGetLocalPos();
rotation newRot = llGetLocalRot();

if ((llVecDist(currPos, newPos) > DIST_TOLERANCE) ||
(llRot2Angle(newRot/currRot) > ANGULAR_TOLERANCE))
{
currPos = newPos;
currRot = newRot;
sendPosUpdate();
}
}
}


Put the following script in any prims that need to move relative to a virtual root prim, adding the rest of your required logic. Note you'll have to change VIRTUAL_ROOT_PRIM_NUM to match the link number of the virtual root prim, or somehow track it dynamically if that's your aim.

CODE

//// Local Position Synch Header

// string arg is: <newLocalPos>,<newLocalRot>
integer LOCAL_POS_CHANGED_LINK = -386335658;


//// Constants

// Change this to match your situation, or change script to track it
// dynamically somehow
integer VIRTUAL_ROOT_PRIM_NUM = 3; // CHANGE


//// Variables

vector virtualRootLocalPos = ZERO_VECTOR;
rotation virtualRootLocalRot = ZERO_ROTATION;


//// Functions

setPosRotRelativeTo(integer virtualRootPrimNum, vector relPos, rotation relRot)
{
vector newLocalPos = (virtualRootLocalPos+relPos)*virtualRootLocalRot;
rotation newLocalRot = relRot*virtualRootLocalRot;

// NOTE: If you want to do this in one call set llSetPrimitiveParams(),
// you'll have to do a little extra work to account for SVC-93.
// See http://jira.secondlife.com/browse/SVC-93
llSetPos(newLocalPos);
llSetLocalRot(newLocalRot);
}


//// States

default
{
// ...

link_message(integer senderPrim, integer cmd, string stringParam, key keyParam)
{
if (cmd == LOCAL_POS_CHANGED_LINK && senderPrim == VIRTUAL_ROOT_PRIM_NUM)
{
list posData = llCSV2List(stringParam);
virtualRootLocalPos = (vector)llList2String(posData, 0);
virtualRootLocalRot = (rotation)llList2String(posData, 1);
}
}
}


Note that you should NOT use the first script in the actual root prim. If you're going to reuse the same logic in child prims that will position themselves relative to the root, either leave the first script out of the root (if the child prims never change virtual roots) or change the first script in the root prim to ALWAYS send ZERO_VECTOR and ZERO_ROTATION. If you are going to dynamically change virtual roots, you might also have to send updates periodically even if the position hasn't changed significantly (simplifies the logic in the virtual roots, but increases processing and link messages greatly).
Psistorm Ikura
Registered User
Join date: 19 Jul 2006
Posts: 52
02-02-2009 05:57
thanks so much for your time. Ive gone and experimented with that formula, though I cant get it to produce the desired events. Id owe you big time if you could tell me how I can adapt it to work for llSetPrimitiveParams (Id like to avoid multiple calls, because I set size/rotation/position and texture in one call). Im quite ready to dicuss compensations inworld, since thatd be a tremendous help for me. Id try and figure out the math myself, but my skills in vector math simply dont suffice atm.
I thought itd need the addition of llGetRootRotation() in some strategic spots, but apparently its said not to work in attachments, so Im a bit lost now a call via primtiveParams has to look like
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
02-02-2009 10:54
Hmm. Okay. Nevermind what I said before about not putting that script in the root prim. Put it in there too, then use this modified version of the child positioning script. Hopefully this will do it.

Nevermind compensation. Just see if it works, and if not we can maybe tinker some more.

CODE

//// Local Position Synch Header

// string arg is: <newLocalPos>,<newLocalRot>
integer LOCAL_POS_CHANGED_LINK = -386335658;


//// Constants

// Change this to match your situation, or change script to track it
// dynamically somehow
integer VIRTUAL_ROOT_PRIM_NUM = 3; // CHANGE


//// Variables

vector realRootPrimPos = ZERO_VECTOR;
rotation realRootPrimRot = ZERO_ROTATION;
vector virtualRootLocalPos = ZERO_VECTOR;
rotation virtualRootLocalRot = ZERO_ROTATION;


//// Functions

setPosRotRelativeTo(vector relPos, rotation relRot)
{
vector newLocalPos = virtualRootLocalPos+relPos*virtualRootLocalRot;
rotation newLocalRot = relRot*virtualRootLocalRot;

// Account for SVC-93.
// See http://jira.secondlife.com/browse/SVC-93
newLocalRot /= realRootPrimRot;

llSetPrimitiveParams(
[ PRIM_POSITION, newLocalPos, PRIM_ROTATION, newLocalRot ]);
}


//// States

default
{
// ...

link_message(integer senderPrim, integer cmd, string stringParam, key keyParam)
{
if (cmd == LOCAL_POS_CHANGED_LINK)
{
if (senderPrim == 1)
{
list posData = llCSV2List(stringParam);
realRootPrimPos = (vector)llList2String(posData, 0);
realRootPrimRot = (rotation)llList2String(posData, 1);

if (VIRTUAL_ROOT_PRIM_NUM == 1)
{
virtualRootLocalPos = ZERO_VECTOR;
virtualRootLocalRot = ZERO_ROTATION;
}
} else if (senderPrim == VIRTUAL_ROOT_PRIM_NUM)
{
list posData = llCSV2List(stringParam);
virtualRootLocalPos = (vector)llList2String(posData, 0);
virtualRootLocalRot = (rotation)llList2String(posData, 1);
}
}
}
}
Psistorm Ikura
Registered User
Join date: 19 Jul 2006
Posts: 52
02-02-2009 14:37
hmm... for some reason, Im still having no luck. my one suspicion is that maybe something with relRot is wrong. relRot/relPos are obtained by calling llGetLocalPos() / llGetLocalRoot() in the prims that I get my data of (basically, I have a linkset with all prims in all positions. then I compile that data into sorted lists, so I need maybe 5 prims, which can be moved/rotated/etc into the corresponding position. the root rotation should affect /getting/ the data. so setting it, they shouldnt be messed up by it.
also, right now Im having my root at <0,0,90> degrees rotated on the ground, and the prims end up having a 180 degrees rotation with the formula that was suggested.

to somehow add to my dilemma, ive encountered a truly bizzarre error: my prim-moving scripts work after compile, but if I use /5reset (a command which simply calls llResetScript() in all linkset prims), PRIM_SIZE is ignored all of a sudden. Ive checked the size value, exactly before llSetPrimitiveParams() is called, and the sizes are different, but they never change.. Im really a bit worried now about this, LSL seems horridly unreliable in parts, and I /need/ these things to work, of course. multiple calls to PRIM_SIZE are also ignored in the same fashion..

edit: Ive just used llSetScale() instead. i wanted to avoid doing the movements in two calls, but after some brief testing, its fast enough not to be noticeable. that just leaves the issues with rotations. Ill look further into the whole thing, and try and figure out if I cant get it working the way it should
Psistorm Ikura
Registered User
Join date: 19 Jul 2006
Posts: 52
02-03-2009 12:51
Ive found the mistake, there was a stupid bug in an if() condition that caused both the virtual and the real root data to be set to real root values.

it works around 95% now. thers some inaccuracies that I cant work my head around. they arent of a rotational nature, but positional. rotation is spot on and working. position is offset by around 0.02 from where it should be, and I cant quite account for where this comes from, but will continue to look into it, and whether theres a way to a) make the positioning accurate, and b) do the calculation in reverse (reason being, my product will allow the users to mod it. they can move/rotate/scale/etc the prims and save its state back into the script, thus I need to "remove" the offsets first. either that or come up with a different solution that Ill work out. mostly, i have to figure out how to make the calculation accurate :)
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
02-03-2009 13:40
Oh. Hmm. Okay. Mind pointing out where the error was, or posting the fixed version? I'm not seeing anything.
Psistorm Ikura
Registered User
Join date: 19 Jul 2006
Posts: 52
02-04-2009 07:15
ah, your version was fine, I made a mistake implementing it into my own scripts :)
that leaves only the inaccuracy error to be solved, Im looking into it some more
Psistorm Ikura
Registered User
Join date: 19 Jul 2006
Posts: 52
02-17-2009 15:05
Im adding this post for completeness, since the issue has been resolved and might help someone else in the future:
there was a tiny error in the function used, instead of:
vector newLocalPos = (virtualRootLocalPos + relPos) * virtualRootLocalRot;

it needs to be:
vector newLocalPos = virtualRootLocalPos + (relPos * virtualRootLocalRot);
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
02-17-2009 15:54
Ah. Cool. Right you are. I'll edit the script above to include that fix, just in case anyone else uses it.