Quaternion rips and tricks
|
|
Lee Ponzu
What Would Steve Do?
Join date: 28 Jun 2006
Posts: 1,770
|
12-17-2006 10:01
Like a lot of people, I've been struggling with lsl rotations, aka quaternions. I've googled and wiki'd and experimented. I feel like I am finally starting to get a grip...
Below, I will post a solution to a simple but real problem that I wrestled with. It appears elsewhere, but I will put it here.
My idea is that others will post their solutions to LSL problems involving rotations, vectors, and closely related issues, so that this might someday evolve into a sticky.
|
|
Lee Ponzu
What Would Steve Do?
Join date: 28 Jun 2006
Posts: 1,770
|
Finding a point offset from current position
12-17-2006 10:27
Often, you have an object that has a current position and rotation. You need to calculate a new point that is offset from that object, for example 1 meter directly ahead. Here's a way to do it, plus an explanation of how it works. An object Foo has a current position that can be retrieved using vector p = llGetPos(); This returns the position of Foo in world coordinates. Foo also has a rotation that represents its heading or orientation with respect to the world coordinates. rotation r = llGetRot(); This r is the infamous quaternion. For LSL purposes, you might not need to understand all the underlying math. It is just the rotation represented in 4 numbers. Note that by definition, Foo's x coordinate is forward, the y coordinate is to the left, and the z coordinate is up. This is a so-called right hand system. Point in the x direction, point your middle finger at right angles in the y direction, and then your thumb points up. From the point of view of Foo, he vector representing "1 meter ahead" is <1,0,0>, but this vector is in local coordinates. If you click the Local in the Object Editor, the x-axis is by definition the "front" of the object. These are the local axes. When World is selected, the region axes are shown. Suppose you tried to compute the desired new possition by vector newPos = p + <1,0,0>; // wrong The problem is that this adds 1 meter to the current position of Foo in the world coordinates, which is 1 meter to the East, no matter how the object is rotated. This is where the magic of quaternions comes in. If we multiply a local vector on the right by the object's rotation, it is converted into the needed world vector. So, what we need is vector newPos = p + <1,0,0> * r; // right The position, p, is already in world coordinates. <1,0,0> is in local coordinates, and multiplying it on the right by the rotation, r, converts it to world. (On the left would not work. That's the way quaternions are.) Suppose you need a point offset differently, such as 1 meter ahead, but 2 meters higher. Then use <1,0,2> as the offset in local coordinates. In general llGetPos() + offset*llGetRot() will be the point offset in local coordinates from the current position. I hope this is right. Other examples welcome.
|
|
Lee Ponzu
What Would Steve Do?
Join date: 28 Jun 2006
Posts: 1,770
|
Lining up
12-17-2006 10:29
If you want to objects to be oriented the same way, you can use the rotations. rotation r = llGetRot(); // the rotation of one object.
llSetRot(r); // in the other object, causes it to be rotated the same way.
That is, local x, y, and z axes will be the same. However, this is for non-pysical objects, and is more or less instantaneous. For physical objects, try something like llRotLookAt( r, .1, 1); Can be used. The rotation is the same. The second parameter is how fast it rotates, and the third is how quickly it stops rotating once it gets there.
|
|
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
|
12-17-2006 10:40
May i suggest you put any code snippets in brackets so it's nicely highlighted?
I think this has great potential to be a sticky.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river. - Cyril Connolly
Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence. - James Nachtwey
|
|
Lee Ponzu
What Would Steve Do?
Join date: 28 Jun 2006
Posts: 1,770
|
Rotate to face a point
12-17-2006 10:41
Suppose you have an <x,y,z> point in world coordinates, and an object's Foo. You want Foo to rotate to face the point. You need to calculate the rotation needed to convert Foo's rotation by.
I don't feel that I have a good solution to this. Any advice welcome.
|
|
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
|
12-17-2006 10:50
vector foo_pos; //equals position of foo in region cords vector my_pos = llGetPos();
vector axis = <1.0,0.0,0.0>;//which axis to point at foo. rotation target = llRotBetween(axis, foo_pos - my_pos); llSetRot(target);
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river. - Cyril Connolly
Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence. - James Nachtwey
|
|
Vares Solvang
It's all Relative
Join date: 26 Jan 2005
Posts: 2,235
|
12-17-2006 11:48
I have a "wander'n cube" that I made up that uses neutral buoyancy to float and then just applied an impulse in a random direction every now and then. It works fine and is fun to just wander around sl on, but I have never been able to figure out a way to make it rotate to face the new, random direction that the impulse is applied too. Do you know a way to do this? Here is the code I used. The llLookAt line didn't work btw (of course). Please note this was just something I banged together one night while I was bored and is in no way "finished" code.  : integer x; float xaxis; float yaxis; float xrand; float yrand; float mass; vector turn = <0,0,0>; vector mypos;
default { state_entry() { llSay(0, "Ensign, lay in a course for adventure!"); //llSetStatus(STATUS_ROTATE_X | STATUS_ROTATE_Y | STATUS_ROTATE_Z, FALSE); llSetPos(llGetPos() + <0, 0, 0.1>); llSetPrimitiveParams([PRIM_PHYSICS, TRUE]); mass = llGetMass(); }
touch_start(integer total_number) { llSetTimerEvent(60); llSay(0, "Engage!"); llSetHoverHeight(65, TRUE, 10); llApplyImpulse(mass * <-10,0,0>, FALSE);
} timer() { xaxis = (llFrand(5)); yaxis = (llFrand(5)); xrand = (llCeil(llFrand(10))); yrand = (llCeil(llFrand(10))); if (xrand > 5) { xrand = -xrand; } if (yrand > 5) { yrand = -yrand; } llApplyImpulse(mass * < xaxis, yaxis, 0>, FALSE); mypos = llGetPos(); llLookAt(mypos + llGetVel(), mass * 0.5, mass * rotation_modifier); llSay (0, "Engage!"); if (x == 30) { llDie(); } else x=x+1; llSay (0, (string) x + " Mintues have passed,"); llSay (0, (string) (30-x)+ " Minutes left till I die."); } }
|
|
Lee Ponzu
What Would Steve Do?
Join date: 28 Jun 2006
Posts: 1,770
|
Untested solution to Vera's problem
12-17-2006 12:26
So, this problem is, make the rotation of an object line up with it's velocity. llGetPos() + llGeVel() is a point in world coordinates, just like the previous problem. That's where your object is headed. To make the x-axis point at that point, try vector tgt=llGetPos()+llGetVel(); vecttr pos=llGetPos();
llSetRot( llRotBetween(<1,0,0>, tgt-pos)) );
Looking at the math, that turns out to be llSetRot( llRotBetween(<1,0,0>, llGetVel())) ;
|
|
Vares Solvang
It's all Relative
Join date: 26 Jan 2005
Posts: 2,235
|
12-17-2006 16:43
I replaced the llLookAt line with your code, but I still don't rotate at all when the random impulse is applied. Does this kind of code only work if you are using if you are using llSetVel type code with it?
|
|
Joannah Cramer
Registered User
Join date: 12 Apr 2006
Posts: 1,539
|
12-17-2006 18:11
From: Lee Ponzu The problem is that this adds 1 meter to the current position of Foo in the world coordinates, which is 1 meter to the East, no matter how the object is rotated. This is where the magic of quaternions comes in. If we multiply a local vector on the right by the object's rotation, it is converted into the needed world vector. So, what we need is vector newPos = p + <1,0,0> * r; // right Alternative approach would be, there's functions in LSL which allow you directly extract local forward, up and left vectors from provided rotation. Such vector is 1 m long so in this particular example, the code would be simply: vector newPos = p + llRot2Fwd( r );
|
|
RobbyRacoon Olmstead
Red warrior is hungry!
Join date: 20 Sep 2006
Posts: 1,821
|
12-17-2006 19:33
Sometimes you want to know if something is in front of you or, conversely, whether another object is facing you. It is easy enough to know whether you are facing some other point, and often a half-sphere sensor sweep is all you need for that, but what about when some other object or avatar is facing *you*? To use an example, consider that you are writing a swordfighting script and need to determine whether your opponent, who is blocking, is facing away from you. You might wish to know this so that your opponent cannot block attacks from behind. There are several other examples that come to mind quickly enough  Below is some code that can determine either case (is it in front of me, or is it facing me) by turning a position and rotation into a plane equation and testing another position to see whether it is on the positive halfspace of that plane. Note that both cases are practically identical, just re-ordering the inputs in the equation. integer isTargetInFrontOfMe( rotation myDir, vector myPos, vector targetPos ) { // Convert dir and pos to a plane equation vector p1 = myPos + <0,1,0> * myDir; vector p2 = myPos + <0,0,1> * myDir; vector normal = llVecNorm( (p2 - p1) % (myPos - p1) ); vector dv = llVecNorm( targetPos - p1 ); float test = normal * dv;
return (test > 0); }
integer isTargetFacingMe( vector myPos, vector targetPos, rotation targetDir ) {
// Convert dir and pos to a plane equation vector p1 = targetPos + <0,1,0> * targetDir; vector p2 = targetPos + <0,0,1> * targetDir; vector normal = llVecNorm( (p2 - p1) % (targetPos - p1) ); vector dv = llVecNorm( myPos - p1 ); float test = normal * dv;
return (test > 0); }
default {
touch_start(integer total_number) { llOwnerSay( "In front = " + (string)isTargetInFrontOfMe( llGetRot(), llGetPos(), llDetectedPos(0) ) ); llOwnerSay( "Facing me = " + (string)isTargetFacingMe( llGetPos(), llDetectedPos(0) , llDetectedRot(0) ) ); } }
This is almost certainly not the most efficient method, but it is adequate for many purposes.
|
|
Joannah Cramer
Registered User
Join date: 12 Apr 2006
Posts: 1,539
|
12-17-2006 20:36
From: RobbyRacoon Olmstead // Convert dir and pos to a plane equation vector p1 = myPos + <0,1,0> * myDir; vector p2 = myPos + <0,0,1> * myDir; vector normal = llVecNorm( (p2 - p1) % (myPos - p1) );
This bit can also be simplified to vector normal = llRot2Fwd( myDir );
^^
|
|
Lee Ponzu
What Would Steve Do?
Join date: 28 Jun 2006
Posts: 1,770
|
Your object is physical....
12-17-2006 21:36
From: Vares Solvang I replaced the llLookAt line with your code, but I still don't rotate at all when the random impulse is applied. Does this kind of code only work if you are using if you are using llSetVel type code with it? Looking at your code, you can see that your object is physical. llSetRot() works on non-physical objects. Try using llRotLookAt(r, f, d); f is how hard to push to make your object run (try 0.1 or 0.2) and d is how quickly the turning stops (try 1.0 or 2.0).
|
|
Lee Ponzu
What Would Steve Do?
Join date: 28 Jun 2006
Posts: 1,770
|
12-17-2006 21:51
From: RobbyRacoon Olmstead Sometimes you want to know if something is in front of you or, conversely, whether another object is facing you. float test = normal * dv; return (test > 0); Can someone explain why test>0 translates to "in front of"? normal points forward, right? dv seems to point from about face level toward the other guy. So, what does normal*dv>0 mean?
|
|
RobbyRacoon Olmstead
Red warrior is hungry!
Join date: 20 Sep 2006
Posts: 1,821
|
12-18-2006 00:34
From: Joannah Cramer This bit can also be simplified to vector normal = llRot2Fwd( myDir );
^^ Neat! Thank you!
|
|
RobbyRacoon Olmstead
Red warrior is hungry!
Join date: 20 Sep 2006
Posts: 1,821
|
12-18-2006 09:47
From: Joannah Cramer This bit can also be simplified to vector normal = llRot2Fwd( myDir );
^^ So, using that change, the test becomes integer isTargetFacingMe( vector myPos, vector targetPos, rotation targetDir ) {
vector normal = llRot2Fwd( targetDir ); vector dv = llVecNorm( myPos - targetPos ); float test = normal * dv;
return (test > 0); }
I tried this, it seems to work wonderfully. I am quite pleased about the optimization, thank you.
|
|
Joannah Cramer
Registered User
Join date: 12 Apr 2006
Posts: 1,539
|
12-18-2006 11:10
From: Lee Ponzu Can someone explain why test>0 translates to "in front of"?
normal points forward, right? dv seems to point from about face level toward the other guy.
So, what does normal*dv>0 mean? the vector * vector part returns vector dot product, which is --if i recall right-- cosine of angle formed by these two vectors. This means as long as the returned value remains in (1, 0) range, the angle formed by vectors is less or equal 90 degree. When the value is in range (0, -1) this means the vectors are at angle greater than 90 degree which can be vaguely translated as being back to back with each other.
|
|
Seifert Surface
Mathematician
Join date: 14 Jun 2005
Posts: 912
|
12-18-2006 12:56
If a and b are vectors then a * b == llVecMag(a) * llVecMag(b) * llCos(the angle between a and b). If all you care about is the sign of the dot product, then it doesn't matter if the vectors are unit length or not. If you're after the angle itself via the cosine of it, then better throw in some llVecNorms here and there.
_____________________
-Seifert Surface 2G!tGLf 2nLt9cG
|
|
Nuen Hyun
Registered User
Join date: 12 Apr 2007
Posts: 14
|
My Problem
04-21-2007 11:51
In the interest of learning more about LL, I decided to add a sit script to a simple seat. I thought it would be as simple as taking an existing sit script and modifying the rotation or sit orientation. The seat is a hollow cylinder rotated 90 degrees and cut till there is about 45'ish degrees left to sit on. Sitting on it with ZERO_ROTATION I appear to sit on the rotated cylinder (imagine the original rotated 90 degrees). I tried to do a simple rotation to get me back on the seat, but I am missing something. Perhaps an axis translation? Can anyone help clear things for me?
Thank you.
|
|
Lee Ponzu
What Would Steve Do?
Join date: 28 Jun 2006
Posts: 1,770
|
more data needed
04-22-2007 09:09
...but your logic seems right. Sit with ZERO_ROTATION, see how it looks, and then change the rotation in the llSetSitTarget() to adjust.
Did you use radians or degrees?
|
|
Nuen Hyun
Registered User
Join date: 12 Apr 2007
Posts: 14
|
04-22-2007 11:37
I figured it out, or at least what works. Did more searching and found http://wiki.secondlife.com/wiki/Rotation to help. My mistake was, I left the 's' parameter of the rotation as 1.0 not knowing what to do with it. To fix, I used 'Euler2Rot ' and supplied only my rotations. With this, I got my Avitar to sit where I wanted him to.
|
|
Teddy Qinan
Registered User
Join date: 10 Mar 2007
Posts: 34
|
11-22-2007 15:37
How do I find the direction an object is facing flattened to the x or y axis? ie, if my object's rotation is rot, what compass direction is it facing?
Edit: I originally wrote "flattened to the z-axis" but of course that wouldn't be flat.
|
|
Tyken Hightower
Automagical
Join date: 15 Feb 2006
Posts: 472
|
11-22-2007 18:13
From: Teddy Qinan How do I find the direction an object is facing flattened to the z-axis? ie, if my object's rotation is rot, what compass direction is it facing? vector direction = llRot2Fwd(llGetRot()); You can set direction.z to 0.0 and thus it will be 'flat.'
|
|
Void Singer
Int vSelf = Sing(void);
Join date: 24 Sep 2005
Posts: 6,973
|
11-22-2007 21:40
From: Teddy Qinan How do I find the direction an object is facing flattened to the x or y axis? ie, if my object's rotation is rot, what compass direction is it facing? Edit: I originally wrote "flattened to the z-axis" but of course that wouldn't be flat. by ignoring them list vListCompassPoints = ["W", "S", "E", "N", "W"]; vector vVecCompass = llRot2Euler( llGetRot() ) * RAD_TO_DEG; integer vIntPoint = vVecCompass.z / 90 + 2; string vStrFacing = llList2String( vListCompassPoints, vIntPoint ); the code can be simpler, but I think you get the idea... rot 2 euler returns values from - to + 180, to avoid inversions of other axis'... divide by the degrees between compass points for a range of options, shift the range into the usable format and select the matching option from a list... note since +/-180 is the same direction, you may need to have it in the list at both extremes, I can't remember is the equation prefers one or another, so expect both using the above mentioned rot2fwd you'd similarly discard the x and y elemnets, and only use the z... I think fwd for the purpose of that call is East (+X ), and you might need to reverse the list order....
_____________________
| | . "Cat-Like Typing Detected" | . This post may contain errors in logic, spelling, and | . grammar known to the SL populace to cause confusion | | - Please Use PHP tags when posting scripts/code, Thanks. | - Can't See PHP or URL Tags Correctly? Check Out This Link... | - 
|
|
Teddy Qinan
Registered User
Join date: 10 Mar 2007
Posts: 34
|
11-22-2007 22:51
Thanks for the compass scripts, they do work well. Although I don't think I explained myself properly. I was meaning compass direction in degrees, and hoping for a combination of functions that would spit out a straight number and save me from having to use If statements to check for which quarter of the compass the prim was pointing. Maybe it can't be done?
|