Difference between revisions of "Talk:Rotation"

From Second Life Wiki
Jump to navigation Jump to search
Line 834: Line 834:
:It can give you a halfway rotation, if you're willing to normalize it yourself. You can usually get away without normalizing, if you just want to set. --[[User:Cerise Sorbet|Cerise Sorbet]]
:It can give you a halfway rotation, if you're willing to normalize it yourself. You can usually get away without normalizing, if you just want to set. --[[User:Cerise Sorbet|Cerise Sorbet]]
Adding two rotations makes no sense whatsoever imho [[User:Aleric Inglewood|Aleric Inglewood]] 08:05, 23 March 2014 (PDT)
Adding two rotations makes no sense whatsoever imho [[User:Aleric Inglewood|Aleric Inglewood]] 08:05, 23 March 2014 (PDT)
:Yeah, it's pointless without more functionality. I've asked LL in the past for more in the way of rotation functions and operators (like multiplying/dividing them by a scalar, normalization, etc) but they have never bit. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 17:02, 23 March 2014 (PDT)

Revision as of 17:02, 23 March 2014


Fixed a few minor syntax errors and reworked the top of the article a bit. As an article it is quite far along though it still has a ways to go. Strife Onizuka 03:32, 29 January 2007 (PST)

Intro is awful

"gymbal lock" is something that affects mechanical systems with gyroscopes. A quaternion rotation is a mathematical abstraction. The term simply does not apply, and only serves to confuse things. The reference to "SLERP" without a link is useless. Doran Zemlja

Gimbal lock effects Euler angles to which you wrote an article using them everywhere without mention it; it is a gross oversight. That sentence is a bit complex on review and yes there is no SLERP article but that doesn't mean someone won't write one (it is better to leave open links then no links; no links give no hint that a page should be created). I may write a short one tomorrow, I already have a function that implements it (I've just been pressed for time). I will find a better article on gimbal lock, the wikipedia one is pretty poor in this regard as it fails to go into the programing implications. I gave you the benefit of the doubt and looked up 'gymbal' in the dictionary, to which it was nowhere to be found; but 'gimbal' was. Strife Onizuka 19:33, 31 January 2007 (PST)
Gimbal lock affects you every time you use Euler Coordinates, it is not restricted to gyroscopes. German article on wikipedia about gimbal lock shows it much more clearly than the English version -- Catherine Pfeffer 2007-12-08

I'm a mathematician, and think the following text is also misleading (or partially incorrect) in the Other Representations section: "Another way to represent a rotation is using three numbers, <X, Y, Z>, which represent the amount (in radians) which the object is rotated around each axis. This is used in the Edit window..." Technically, 1 "radian" equals the angle that is traced out by an arc along a circle's circumference, when that arc equals the circle's radius. This means that 2*pi radians equals 360 degrees, 1*pi radians equals 180 degrees, and pi/2 radians equals 90 degrees. SL's prim-editing window displays rotations in degrees (360, 180, 90...) and NOT in radians (2pi, pi, pi/2)! However, it IS true that radians must be used when editing values in some LSL script commands! Times Sands 18:18, 10 November 2008 (UTC)

Mirroring using quaternions

I think I have worked out how to mirror a point (and therefore an entire object in pieces,) using quaternions, but does anyone here know how to, or if it is even possible to, define a rotation that turns a left hand into a right hand or vise-versa? This is a three diminsional mirror operation, but in four diminsions it is a simple rotation. (I could be wrong, but based on my research into quaternions it seems right...)
Cron Stardust 22:16, 25 March 2007 (PDT)

It depends entirely upon which axis you want to mirror around. Save yourself some work and look up Jeffrey's Prim Mirror. Strife Onizuka 11:13, 26 March 2007 (PDT)

Moving this page

Shouldn't the content of this page be moved to Category:LSL Rotation? It seems confusing to me to have multiple Rotation pages.

Good point, I see no problem merging them. -- Strife Onizuka 08:40, 22 July 2007 (PDT)
Very true. Or even better, do a "pure maths" one and a "pure lsl" one -- Catherine Pfeffer, 2007-12-08

GetRot()/SetRot() chart

llGetLocalRot() and llGetRot() clearly do *not* return the same value in the root prim of an attachment. While I believe that llGetRot() returns the avatar rotation as indicated, llGetLocalRot() I believe gives the rotation of the root prim relative to the attachment point (or the avatar?)

You are correct, that entry in the table is wrong. -- Strife Onizuka 08:40, 22 July 2007 (PDT)

Visual aids

I use this page as a visual reference and cheat sheet for quaternion operations. --Tateru Nino 15:53, 9 September 2007 (PDT)

Maths versus LSL

Has anyone noticed that maths' quaternion multiplication is written in reverse order than in LSL ? That is, what you write Q1 . Q2 in maths is computed in LSL with Q2 * Q1. Is that a bug or simply a documentation issue ? -- Catherine Pfeffer - 2007-12-08

It appears to be a bug, with an obvious patch in the server code, but this bug won't be fixed, so I decided to document the problem here -- Catherine Pfeffer - 2008-08-28
I know it's a way late comment, but my guess is that it's the noticable side effect of LSL's backwards order of evaluation. we just don't notice it elsewhere because most other operations within lsl are communicative and quaternion math isn't.
-- Void (talk|contribs) 11:09, 19 May 2009 (UTC)

Unjustified preference for Euler notation

One thing that really annoys me with this page is that it seems to consider that the Euler coordinates are the easy way to deal with rotations, while the <x, y, z, s> quaternion representation is almost unintelligible.

That's a common opinon, and as a matter of facts, most LSL scripts I have found use and overuse llEuler2Rot() and llRot2Euler().

But that's simply wrong. Euler's notation is completly unintuitive for any non-trivial situation.

To get convinced of it, try to figure out the Euler angles for the rotation of 45 degrees around XY diagonal (X = Y and Z = 0). Good luck ! :-). Now try figuring out the quaternions's values with the directing vector and sine and cosine of the half-angle: that's straightforward.

I think this page is misleading a lot of new scripters into the wrong direction because of its preference for Euler angles. -- Catherine Pfeffer - 2008-08-28

I agree. Strife (talk|contribs) 14:19, 28 August 2008 (PDT)
As I also agreed and no one here has disagreed, I have take the liberty of adding a section on axis and turn angle. I think it's horses for courses whether you are better off using Euler2Rot or AxisAngle2Rot, but people should be more aware of both. I also put an example in the section on degrees to radians. If anyone wants to champion forward, left, right representation, it won't be me! Jonno Stromfield 12:21, 21 October 2009 (UTC)

Rotating Vectors

The example:-

//-- same as: llSetPrimitiveParams( [PRIM_POSITION, llGetPos() + (gPosOffset - gPosOffset * gRotArc) * llGetRot(),PRIM_ROTATION, gRotArc * llGetRot()] );

gives no derivation of the variables gPosOffset and gRotArc. Which is less than useful to someone learning from the text. Gregory McLeod 08:51, 1 September 2008 (PDT)

missed this comment before. although it should be guessable that offset = gPosOffset, and rot6x = gRotArc, I went ahead and made the section more explicit, and converted it to use lsl highlighting (so that all keywords would be linked) and removed a few slightly misleading viewer frame references.
-- Void (talk|contribs) 11:00, 19 May 2009 (UTC)

Useful snippets

The snippet we have now is kind of a string function. How about converting these from XSI to LSL?

How about these from lslwiki.net? ("All functions on this page are released into public domain and may have been based off other public domain works.")

By the way, someone above complained about an unlinked reference to slerp() which seems to now be completely missing(!) Both of those have slerp(). Gia May 07:15, 8 December 2008 (UTC)

I offer L$5000 to the first person who translates two functions from http://www.isner.com/tutorials/quatSpells/quaternion_spells_12.htm to the Useful snippets here, or for code constraining rotations to an arbitrary conic section. Runners up, if any, will be awarded on merit, and I am also asking the Lindens to help with this library deficiency bug bounty. IM me inworld for details. JS Uralia 21:17, 2 February 2009 (UTC)
Hello, I went ahead and added three such functions: ConstrainQuaternion2Plane, ScaleQuaternion and CombineQuaternions (Slerp) though this last one already existed on the Wiki.--Jesrad Seraph 07:05, 17 May 2009 (UTC)

Pending fulfillment of my promise to pay Jesrad L$5000 -- and Mephistopheles Thalheimer can attest I keep my word on these bounty payments -- could someone please independently verify that this function works as it should?

<lsl>// Constrain a rotation to a given plane, defined by its normal, this is very useful for vehicles that remain horizontal in turns: rotation ConstrainQuat2Plane(rotation source, vector normal) { return llAxisAngle2Rot(normal, <source.x, source.y, source.z> * normal * llRot2Angle(source)); } // Jesrad Seraph</lsl>

I'm very troubled by the use of the first three elements of the quaternion in that manner, but in all honestly, I don't know enough to figure out what a correct answer is. JS Uralia 20:43, 23 May 2009 (UTC)

I think it might just be correct. I can't verify it at the moment but the math looks to have the correct properties though the magnitudes of the vectors going into the dot product worry me. I'm wondering if <source.x, source.y, source.z> shouldn't be normalized first. On a side note, it doesn't handle the edge cases where source and normal haven't been normalized. -- Strife (talk|contribs) 09:56, 24 May 2009 (UTC)
More testing shows it is off by sometimes as much as 90 degrees... I will try to correct it, probably by normalizing the vector part first like suggested.--Jesrad Seraph 14:52, 7 July 2009 (UTC)
Any progress? I just ran into some problems with it myself, so I left a note on the main page warning others to be cautious with it. Nica Pennell 03:34, 23 November 2009 (UTC)

With regard to rotations of a child of an attachment

I blow a raspberry at lsl. -- Eddy (talk|contribs) 06:37, 13 July 2009 (UTC)

Don't you just love SVC-93? Gage me with a spoon *rolls eyes*. -- Strife (talk|contribs) 18:08, 13 July 2009 (UTC)

Ah. Not just me then. I thought I just needed a degree in quantum maths to understand it. Thanx for the link. *voted* -- Eddy (talk|contribs) 01:41, 14 July 2009 (UTC)


I think this is wrong: it claims that the llGetRot of a child prim of an attached object gives "global rotation of avatar * global rotation of prim (Not Useful)" This would be very useful if it were true and would let you calculate the rotation of the attach point. But I think llGetRot gives you the local rotation of the child prim * global rotation of the avatar, which is of no use that I can think of. I'm new at scripting so I'd appreciate some verification and so won't just change the page. Jonno Stromfield 11:04, 11 September 2009 (UTC)

Simplification of the page

Having just head-bashed and hair-pulled my way through yet another case of what should be a simple rotation task, I've come to the understanding that despite all the information in the rotations page, that it really is a poor resource for people that just want to get things done. While it's all well and good knowing various tidbits about rotations, 99% of scripters just need to know how to use them, as such it may be a good idea to re-write the article as more of a practical manual, with the emphasis being on useful code snippets. The things that I think most scripters want to know (and some of which are currently covered, albeit in a bit of a text-heavy way) are:

* Combining two rotations (getting a bigger or smaller rotation by combining two together)
* Creating a "negative" rotation (only one, best-practise solution is needed, which I believe is flipping .s?)
* Applying rotations to object (best practises for rotating an object X degrees globally, X degrees locally, as well as -X degrees globally and -X degrees locally)
* Rotating an object around a global point
* Rotating a child-prim around a global point
* Rotating a child-prim around a local point

Each snippet or set of snippets should show how to do these operations using llSet* and llSetLocal* functions, as well as llSetPrimitiveParams(). Basically the idea is that we need the article to be more of a "how-to", or possibly create a new article that is a "cheat sheet", because rotation problems are annoyingly common, and the combination of lack of understanding of quarternion math, and LSL's broken rotation functions does not serve to help anyone, meaning most people looking here are looking for solutions to common problems that they can combine. I've been scripting in Second Life for nearly 5 years, and have a Masters degree in computer science, and my fall-back if I get stuck is still trial and error! I may start hashing together an idea of what such a page should be like as a personal sub-page, but I'm not the most qualified person to provide all of the snippets as I suspect I use bad-practises for most of the above operations, and I'm still having ongoing problems with rotating child-prims around local points without converting to global and doing it that way. -- Haravikk Mistral 12:44, 30 September 2010 (UTC)

best practice for reversing a rotations direction (negative rotation) is actually (ZERO_ROTATION / Rot2Reverse), although there are cases in which scalar sign flipping might be more elegant, that's actually an abuse that does not produce a properly normalized rotation (I, unfortunately, popularized that one =X). applying a rot best practice, assuming the greatest degree of flexibility is desired, is the same method as rotation combining... get the rotation of the prim in the frame that it will be applied in, and then multiply it in the order that produces either a prim relative, or frame relative manner. there are actually ~8+ ways to rotate a prim around a point, depending on if that point is relative to the prim rotating (offset), or to the frame it's in (fixed), whether the calculation is done from the objects perspective, or the points perspective, and whether you want to track the current value by the position or rotation of the point.... 2 are detailed on the rotation page linked from my user page. Rotating a child around a global point is is a slightly odd case that could possibly break much more easily than simply rotating a child around a local point, but the format is almost exactly the same.... I'd be happy to detail extended examples for all of those though (and I promise not to use overly obscure variable names).... I've actually been wanting to update the rotations page under my user page for a while anyways, so I'll detail them there, and you can feel free to pirate my examples for your prospective page design? it may be a week or so before I get to it though.
-- Void (talk|contribs) 08:30, 2 October 2010 (UTC)
Well I'm probably going to be about the same unless I get some time today or tomorrow; as I say I'm not really up on the best way to do everything, as my knowledge is mostly purely practical as I'm sure is the case with most scripters, so I think a "cheat sheet" will help greatly. Coincidentally, I've actually always used rotReversed = (ZERO_ROTATION / rot), seems I also fooled into thinking negative .s might have been simpler, so at least it seems I haven't been completely wrong all this time after all! Anyway, whenever I find time I'll create a page and try to come up with a logical order, then start filling out the examples I can be sure of getting right, and update whenever I get to some meaningful stage.
-- Haravikk (talk|contribs) 10:30, 2 October 2010 (UTC)
m'kay, I'm a nitwit... somehow I thought this conversation happened on the blogorums, and I was trying to figure out who I needed to let know that I'd be updating my rotations page (and make sure I covered all the cases listed)... anyways, yeah... that will happen this week and now that I have the list I can make sure it's all covered.
-- Void (talk|contribs) 12:20, 25 October 2010 (UTC)
Heh, I made the same mistake as well another time myself! Needless to say I haven't got anywhere with my layout as I keep getting distracted with other things (as I am much too prone to doing). Anyway, your page is looking excellent, and thinking on it I'm wondering if perhaps the "cheat sheet" style page will be a good candidate for the Rotation Category page which is currently a bit bleak, so that it has the "live" list of all LSL functions, as well as plenty of cheat-sheet examples. This leaves this article to be all in depth and trying to explain them in more depth, and we just have a prominent link. Am just thinking this as while I come off a bit disparaging about this article, it is actually very good, just not what most scripters want or need to know when a nice set of examples will do just fine.
I also wondered if it might be worth adding mirroring of a rotation to the list of examples; whereby an upper right rotation can be mirrored along your front into an upper left for example. I know it doesn't come up a lot but I know it's something I've never really grasped myself. I always end up just doing things by eye until I get it close enough to round to the correct value, which is very unscientific!
-- Haravikk (talk|contribs) 22:51, 25 October 2010 (UTC)
Look up Jeffrey's prim mirror for the math for mirroring rotations. I forget the details of how to do it aswell (it's been too long since I've done anything but edit the wiki). By all means please improve the rotation category page. I hope the changes to the table are ok with people. -- Strife (talk|contribs) 20:06, 26 October 2010 (UTC)
hmm that should be something I add to the page... IIRC it's just sign flipping of the elements... can't remember the exact sequence, but I'm sure I can trial and error it if I don't have a copy of the prim mirror. I've done some of the page edits for my page already, although I was tired and had to stop, so it's got typos and I haven't redone the examples yet (but you can pull from the previous version in history)
-- Void (talk|contribs) 00:46, 27 October 2010 (UTC)


Position of Object Rotated Around A Relative Point

I found the snippet code for "Position of Object Rotated Around A Relative Point" to be difficult to grasp without some trials and errors (check the lslwiki for a different answer). I made a few tests and i realised that the lslwiki code lacked the adjustment to original object orientation (* llGetRot( )) so the rotation started around a point located to an absolute world offset instead of relative to the object's orientation. I tried to add something to the lslwiki code but i failed to find exactly where to use llGetRot there. I then returned to this page's code and after looking closer i was able to write a small demo that seems to work (I don't have the time to make further tests, i hope someone will check it and see if i didn't make some mistake).
Here is my script that now seems to handle both cases correctly (having one face of the prim (case 1) facing or (case 2) staying in the same rotation all along its orbit toward the center of rotation) : Position of Object Rotated Around A Relative Point (feel free to add the snippet to the wiki page or anywhere you need it if you think its usefull - i don't care for credits, just don't say you made it yourself) (Francoise Eichel 11:11, 22 February 2011 (PST))

<lsl>/*

   Position of Object Rotated Around A Relative Point, 
       from : http://wiki.secondlife.com/wiki/Rotation
   change the FACING value to have the object 
       keep its original orientation all around its orbit (FALSE)
       keep the same face turned toward the center of rotation (TRUE)
  • /

integer FACING = FALSE;

rotation vRotArc; vector vPosOffset;

RotAround( integer facing ) {

   vector curpos = llGetPos();
   vector p = curpos + (vPosOffset - vPosOffset * vRotArc) * llGetRot();
   if( facing )
       llSetPrimitiveParams( [ PRIM_POSITION, p, PRIM_ROTATION, vRotArc * llGetRot() ] );
   else
   {
       llSetPrimitiveParams( [ PRIM_POSITION, p ] );
       vPosOffset = vPosOffset * vRotArc;
   }

}

integer orbiting; default {

   state_entry()
   {
       vRotArc     = llEuler2Rot( <30.0, 0.0, 0.0> * DEG_TO_RAD );
       vPosOffset  = <0.0, 1.0, 0.0>;
   }
   
   touch_start(integer total_number)
   {
       llSetTimerEvent( 2*(orbiting = !orbiting) );
   }
   timer( )
   {
       RotAround( FACING );
   }

}</lsl>

Looks good, two problems:
  • While this method is expedient, the point which the object orbits will drift due to rounding errors. The simple solution is to store the center position in a global.
  • Making vRotArc & vPosOffset globals makes reusing this function difficult (as it has dependencies external to it). Function parameters on the other hand would eliminate this problem. This is a coding style issue and can (and should in this case!) be ignored.
Not only will the position drift, but so will the rotation of the object, removing llGetRot from inside the loop will reduce drift but won't eliminate it. The solution to this problem is to define the axis of rotation and change the angle of rotation. This is also a good opportunity to use states.
Please check that the following code works, I haven't done extensive tested. -- Strife (talk|contribs) 22:22, 22 February 2011 (PST)

<lsl>/*

   Position of Object Rotated Around A Relative Point, 
       from : http://wiki.secondlife.com/wiki/Rotation
   change the bFacing value to have the object 
       keep its original orientation all around its orbit (FALSE)
       keep the same face turned toward the center of rotation (TRUE)
  • /

integer bFacing = FALSE;

integer iDegrees = 30; vector vPosOffset = <0.0, 1.0, 0.0>; vector vRotAxis = <0.0, 0.0, 1.0>;

vector vPosCenter; rotation rRotStart; integer iSteps = 0;

default {

   state_entry()
   {
       vPosCenter  = llGetPos() - vPosOffset;
       rRotStart   = llGetRot();
       state on;
   }

}

state on {

   state_entry()
   {
       llSetTimerEvent( 2.0 );
   }
   touch_start(integer i)
   {
       llSetTimerEvent( 0.0 );
       state off;
   }
   timer()
   {
       iSteps = (iSteps + iDegrees) % 360;//eliminates compounding rounding errors
       rotation rRotArc = llAxisAngle2Rot(vRotAxis, DEG_TO_RAD * iSteps);
       vector dest = vPosCenter + (vPosOffset * rRotArc * rRotStart);
       if( bFacing )
           llSetPrimitiveParams( [ PRIM_POSITION, dest, PRIM_ROTATION, rRotArc * rRotStart ] );
       else
           llSetPrimitiveParams( [ PRIM_POSITION, dest ] );
   }

}

state off {

   touch_start(integer i)
   {
       state on;
   }

}</lsl>

Attached prims: how to get the rotation of the attach point relative to the avatar forward vector

I have no idea how to accomplish that, and I would need to do it in order to reverse all the rotations in an attached linked child prim. My goal is to have one of the linked child prims always pointing in the same direction as the avatar forward vector. I am struggling with this and many many other problems with rotations when dealing with attached prims.

-- Clodoveo Haven 06:40, 13 August 2011 (PDT)

attach points are only relative to themselves, because they are subject to animations (which are client side effcts).... if you make a static animation, then any attachment position will also be static, and you can estimate the facing. the pelvis is an exception, it defaults to the avatar position/facing.
-- Void (talk|contribs) 16:46, 21 August 2011 (PDT)

Useful Tutorials

Grandma Bates has posted a couple of very helpful tutorials at Basic Introduction to Rotations and Introduction to Rotations with Translations (rotation tutorial part II) over at SLU.

Would it an idea to link to them somewhere? Innula Zenovka 07:04, 31 July 2011 (PDT)

even better, get permission to copy and link it as it's own page here?
-- Void (talk|contribs) 08:18, 31 July 2011 (PDT)
I've asked her, and she'll be delighted to have it copied. She's edited the two pieces to add a Creative Commons licence. Innula Zenovka 11:04, 31 July 2011 (PDT)
Sounds like a plan. -- Strife (talk|contribs) 15:16, 31 July 2011 (PDT)

PRIM_ROT_LOCAL & PRIM_POS_LOCAL

Aspects of the article needs to be rewritten since we now have PRIM_ROT_LOCAL and PRIM_POS_LOCAL. -- Strife (talk|contribs) 12:19, 5 September 2011 (PDT)

Some interesting values

What rotations do you get for various Euler coordinates? Derived from LSL and the viewer GUI: <lsl> default { state_entry()

   {   llSetTimerEvent(1.0);   }

timer()

   {   rotation rot = llGetRot();
       llSetText("My rotation: "
           + "\n X: " + (string)rot.x 
           + "\n Y: " + (string)rot.y 
           + "\n Z: " + (string)rot.z 
           + "\n S: " + (string)rot.s
           + "\n \n \n \n \n \n \n \n \n ",
           <1,1,1>,1);
   } 

} </lsl>

Degrees # Rotation
X Y Z # X Y Z S
Starting position:
0 0 0 # 0.000000 0.000000 0.000000 1.000000
Symmetry mystery
60 0 0 # 0.500000 0.000000 0.000000 0.866025
0 60 0 # 0.000000 0.500000 0.000000 0.866025
0 0 60 # 0.000000 0.000000 0.500000 0.866025
90 0 0 # 0.707107 0.000000 0.000000 0.707107
0 90 0 # 0.000000 0.707107 0.000000 0.707107
0 0 90 # 0.000000 0.000000 0.707107 0.707107
120 0 0 # 0.866025 0.000000 0.000000 0.500000
0 120 0 # 0.000000 0.866025 0.000000 0.500000
0 0 120 # 0.000000 0.000000 0.866025 0.500000
Negative and positive 0.000000 ?? ...
180 0 0 # -1.000000 0.000000 -0.000000 0.000000
0 180 0 # -0.000000 -1.000000 -0.000000 0.000000
0 0 180 # -0.000000 -0.000000 -1.000000 0.000000
Is -180 different from 180? Well, that depends on there being a difference between -0 and 0, above...
-180 0 0 # 1.000000 0.000000 0.000000 0.000000
0 -180 0 # 0.000000 1.000000 0.000000 0.000000
0 0 -180 # 0.000000 0.000000 1.000000 0.000000
Quaternions only need half a 4 dimensional sphere to work, the other half is duplicate so, <0,0,0,1> expresses the same angle as <0,0,0,-1>. Mathematically there is no difference between negative and positive zero, but that doesn't mean negative zero is not without it's use which is why it is supported in the floating point standard. As you can see, by preserving the negative zero, you can get seemingly different values, which are just mirror of the same angle. You may want to read Quaternion. -- Strife (talk|contribs) 10:21, 6 December 2011 (PST)

Viewer UI, object rotation bugs, code analysis

So, I think we all know the Viewer UI for rotating objects sucks. Values randomly flip themselves around, and entering angles into one field inexplicably causes the value to disappear and instead the other fields change. Many submissions to the JIRA have resulted in "WON'T FIX" or "EXPECTED BEHAVIOR" responses, with no explanation or proper resolution.

Well, the real answer appears to be to tackle the source directly to figure out why it does this, and what can be done to make its behavior more sane, because nobody at LL seems willing to deal with it themselves.

(The challenge for me personally is that I am not a c++ programmer so I don't fully understand the arcane details of the language. *.h files are apparently for declarations while *.cpp is for definitions, but it seems like if you wade into putting actual code into the .h, beyond simply declaring "int x;" or somesuch, then *.h acts as a *.cpp. But LL has full complete code in .h and apparently conflicting duplicate full complete code in *.cpp. Probably an expert C++ coder would know which parts of this mess is ignored by the compiler.)

object editor panel UI

Starting point, the user interface of the object editor panel:

http://hg.secondlife.com/viewer-development/src/a126abe30997/indra/newview/llpanelobject.cpp

Line 145:

    // Rotation
    mLabelRotation = getChild<LLTextBox>("label rotation");
    mCtrlRotX = getChild<LLSpinCtrl>("Rot X");
    childSetCommitCallback("Rot X",onCommitRotation,this);
    mCtrlRotY = getChild<LLSpinCtrl>("Rot Y");
    childSetCommitCallback("Rot Y",onCommitRotation,this);
    mCtrlRotZ = getChild<LLSpinCtrl>("Rot Z");
    childSetCommitCallback("Rot Z",onCommitRotation,this);

onCommitRotation

Searching for "onCommitRotation" in same file...

http://hg.secondlife.com/viewer-development/src/a126abe30997/indra/newview/llpanelobject.cpp

Line 1951:

// static
void LLPanelObject::onCommitRotation( LLUICtrl* ctrl, void* userdata )
{
    LLPanelObject* self = (LLPanelObject*) userdata;
    BOOL btn_down = ((LLSpinCtrl*)ctrl)->isMouseHeldDown() ;
    self->sendRotation(btn_down);
}

sendRotation

Searching for "sendRotation" in same file...

http://hg.secondlife.com/viewer-development/src/a126abe30997/indra/newview/llpanelobject.cpp

Line 1578:

// BUG: Make work with multiple objects
void LLPanelObject::sendRotation(BOOL btn_down)
{
    if (mObject.isNull()) return;

    LLVector3 new_rot(mCtrlRotX->get(), mCtrlRotY->get(), mCtrlRotZ->get());
    new_rot.mV[VX] = llround(new_rot.mV[VX], OBJECT_ROTATION_PRECISION);
    new_rot.mV[VY] = llround(new_rot.mV[VY], OBJECT_ROTATION_PRECISION);
    new_rot.mV[VZ] = llround(new_rot.mV[VZ], OBJECT_ROTATION_PRECISION);

    // Note: must compare before conversion to radians
    LLVector3 delta = new_rot - mCurEulerDegrees;

    if (delta.magVec() >= 0.0005f)
    {
        mCurEulerDegrees = new_rot;
        new_rot *= DEG_TO_RAD;

        LLQuaternion rotation;
        rotation.setQuat(new_rot.mV[VX], new_rot.mV[VY], new_rot.mV[VZ]);

        if (mRootObject != mObject)
        {
            rotation = rotation * ~mRootObject->getRotationRegion();
        }
        std::vector<LLVector3>& child_positions = mObject->mUnselectedChildrenPositions ;
        std::vector<LLQuaternion> child_rotations;
        if (mObject->isRootEdit())
        {
            mObject->saveUnselectedChildrenRotation(child_rotations) ;
            mObject->saveUnselectedChildrenPosition(child_positions) ;          
        }

        mObject->setRotation(rotation);
        LLManip::rebuild(mObject) ;

        // for individually selected roots, we need to counterrotate all the children
        if (mObject->isRootEdit())
        {           
            mObject->resetChildrenRotationAndPosition(child_rotations, child_positions) ;           
        }

        if(!btn_down)
        {
            child_positions.clear() ;
            LLSelectMgr::getInstance()->sendMultipleUpdate(UPD_ROTATION | UPD_POSITION);
        }
    }
}

F32 LLVector3::magVec

Googling goes to Dale Glass's website which says: ---> F32 LLVector3::magVec () const, in file v3math.h

http://hg.secondlife.com/viewer-development/src/a126abe30997/indra/llmath/v3math.h

Line 337:

inline F32  LLVector3::magVec(void) const
{
    return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
}

(MagVec is not defined in the matching v3math.cpp so this *.h is the declaration and definition.)


const LLQuaternion& setQuat

Understanding this section is a confusing mess for a non C++ programmer. Which of these is actually used by "rotation.setQuat(new_rot.mV[VX], new_rot.mV[VY], new_rot.mV[VZ]);" ?

http://hg.secondlife.com/viewer-development/src/a126abe30997/indra/llmath/llquaternion.h

Line 51:

class LLQuaternion
{
public:

[..........]

    const LLQuaternion& setQuatInit(F32 x, F32 y, F32 z, F32 w);    // deprecated
    const LLQuaternion& setQuat(const LLQuaternion &quat);          // deprecated
    const LLQuaternion& setQuat(const F32 *q);                      // deprecated
    const LLQuaternion& setQuat(const LLMatrix3 &mat);              // deprecated
    const LLQuaternion& setQuat(const LLMatrix4 &mat);              // deprecated
    const LLQuaternion& setQuat(F32 angle, F32 x, F32 y, F32 z);    // deprecated
    const LLQuaternion& setQuat(F32 angle, const LLVector3 &vec);   // deprecated
    const LLQuaternion& setQuat(F32 angle, const LLVector4 &vec);   // deprecated
    const LLQuaternion& setQuat(F32 roll, F32 pitch, F32 yaw);      // deprecated

Line 273:

// deprecated
inline const LLQuaternion&  LLQuaternion::setQuatInit(F32 x, F32 y, F32 z, F32 w)
{
    mQ[VX] = x;
    mQ[VY] = y;
    mQ[VZ] = z;
    mQ[VS] = w;
    normalize();
    return (*this);
}

// deprecated
inline const LLQuaternion&  LLQuaternion::setQuat(const LLQuaternion &quat)
{
    mQ[VX] = quat.mQ[VX];
    mQ[VY] = quat.mQ[VY];
    mQ[VZ] = quat.mQ[VZ];
    mQ[VW] = quat.mQ[VW];
    normalize();
    return (*this);
}

// deprecated
inline const LLQuaternion&  LLQuaternion::setQuat(const F32 *q)
{
    mQ[VX] = q[VX];
    mQ[VY] = q[VY];
    mQ[VZ] = q[VZ];
    mQ[VS] = q[VW];
    normalize();
    return (*this);
}


Overlapping function definitions in the *.cpp:

http://hg.secondlife.com/viewer-development/src/a126abe30997/indra/llmath/llquaternion.cpp

Line 219:

// deprecated
const LLQuaternion& LLQuaternion::setQuat(F32 angle, F32 x, F32 y, F32 z)
{
    LLVector3 vec(x, y, z);
    vec.normalize();

    angle *= 0.5f;
    F32 c, s;
    c = cosf(angle);
    s = sinf(angle);

    mQ[VX] = vec.mV[VX]*s;
    mQ[VY] = vec.mV[VY]*s;
    mQ[VZ] = vec.mV[VZ]*s;
    mQ[VW] = c;

    normalize();
    return (*this);
}

// deprecated
const LLQuaternion& LLQuaternion::setQuat(F32 angle, const LLVector3 &vec)
{
    LLVector3 v(vec);
    v.normalize();

    angle *= 0.5f;
    F32 c, s;
    c = cosf(angle);
    s = sinf(angle);

    mQ[VX] = v.mV[VX]*s;
    mQ[VY] = v.mV[VY]*s;
    mQ[VZ] = v.mV[VZ]*s;
    mQ[VW] = c;

    normalize();
    return (*this);
}

const LLQuaternion& LLQuaternion::setQuat(F32 angle, const LLVector4 &vec)
{
    LLVector3 v(vec.mV[VX], vec.mV[VY], vec.mV[VZ]);
    v.normalize();

    F32 c, s;
    c = cosf(angle*0.5f);
    s = sinf(angle*0.5f);

    mQ[VX] = v.mV[VX]*s;
    mQ[VY] = v.mV[VY]*s;
    mQ[VZ] = v.mV[VZ]*s;
    mQ[VW] = c;

    normalize();
    return (*this);
}

const LLQuaternion& LLQuaternion::setQuat(F32 roll, F32 pitch, F32 yaw)
{
    LLMatrix3 rot_mat(roll, pitch, yaw);
    rot_mat.orthogonalize();
    *this = rot_mat.quaternion();
        
    normalize();
    return (*this);
}

const LLQuaternion& LLQuaternion::setQuat(const LLMatrix3 &mat)
{
    *this = mat.quaternion();
    normalize();
    return (*this);
}

const LLQuaternion& LLQuaternion::setQuat(const LLMatrix4 &mat)
{
    *this = mat.quaternion();
    normalize();
    return (*this);
//#if 1
//  // NOTE: LLQuaternion's are actually inverted with respect to
//  // the matrices, so this code also assumes inverted quaternions
//  // (-x, -y, -z, w). The result is that roll,pitch,yaw are applied
//  // in reverse order (yaw,pitch,roll).
//  F64 cosX = cos(roll);
//    F64 cosY = cos(pitch);
//    F64 cosZ = cos(yaw);
//
//    F64 sinX = sin(roll);
//    F64 sinY = sin(pitch);
//    F64 sinZ = sin(yaw);
//
//    mQ[VW] = (F32)sqrt(cosY*cosZ - sinX*sinY*sinZ + cosX*cosZ + cosX*cosY + 1.0)*.5;
//  if (fabs(mQ[VW]) < F_APPROXIMATELY_ZERO)
//  {
//      // null rotation, any axis will do
//      mQ[VX] = 0.0f;
//      mQ[VY] = 1.0f;
//      mQ[VZ] = 0.0f;
//  }
//  else
//  {
//      F32 inv_s = 1.0f / (4.0f * mQ[VW]);
//      mQ[VX] = (F32)-(-sinX*cosY - cosX*sinY*sinZ - sinX*cosZ) * inv_s;
//      mQ[VY] = (F32)-(-cosX*sinY*cosZ + sinX*sinZ - sinY) * inv_s;
//      mQ[VZ] = (F32)-(-cosY*sinZ - sinX*sinY*cosZ - cosX*sinZ) * inv_s;       
//  }
//
//#else // This only works on a certain subset of roll/pitch/yaw
//  
//  F64 cosX = cosf(roll/2.0);
//    F64 cosY = cosf(pitch/2.0);
//    F64 cosZ = cosf(yaw/2.0);
//
//    F64 sinX = sinf(roll/2.0);
//    F64 sinY = sinf(pitch/2.0);
//    F64 sinZ = sinf(yaw/2.0);
//
//    mQ[VW] = (F32)(cosX*cosY*cosZ + sinX*sinY*sinZ);
//    mQ[VX] = (F32)(sinX*cosY*cosZ - cosX*sinY*sinZ);
//    mQ[VY] = (F32)(cosX*sinY*cosZ + sinX*cosY*sinZ);
//    mQ[VZ] = (F32)(cosX*cosY*sinZ - sinX*sinY*cosZ);
//#endif
//
//  normalize();
//  return (*this);
}


(From looking at the above overlapping code definitions, I would assume the section used is this one, in the *.cpp:)

Line 277:

const LLQuaternion& LLQuaternion::setQuat(F32 roll, F32 pitch, F32 yaw)
{
    LLMatrix3 rot_mat(roll, pitch, yaw);
    rot_mat.orthogonalize();
    *this = rot_mat.quaternion();
        
    normalize();
    return (*this);
}


const LLMatrix3& orthogonalize();

http://hg.secondlife.com/viewer-development/src/a126abe30997/indra/llmath/m3math.h

Line 114:

const LLMatrix3& orthogonalize();   // Orthogonalizes X, then Y, then Z

http://hg.secondlife.com/viewer-development/src/a126abe30997/indra/llmath/m3math.cpp

Line 458:

const LLMatrix3&    LLMatrix3::orthogonalize()
{
    LLVector3 x_axis(mMatrix[VX]);
    LLVector3 y_axis(mMatrix[VY]);
    LLVector3 z_axis(mMatrix[VZ]);

    x_axis.normVec();
    y_axis -= x_axis * (x_axis * y_axis);
    y_axis.normVec();
    z_axis = x_axis % y_axis;
    setRows(x_axis, y_axis, z_axis);
    return (*this);
}

LLMatrix3::quaternion() const

http://hg.secondlife.com/viewer-development/src/a126abe30997/indra/llmath/m3math.h

Line 99:

LLQuaternion quaternion() const;        // Returns quaternion from mat

http://hg.secondlife.com/viewer-development/src/a126abe30997/indra/llmath/m3math.cpp

Line 244:

// SJB: This code is correct for a logicly stored (non-transposed) matrix;
//      Our matrices are stored transposed, OpenGL style, so this generates the
//      INVERSE quaternion (-x, -y, -z, w)!
//      Because we use similar logic in LLQuaternion::getMatrix3,
//      we are internally consistant so everything works OK :)
LLQuaternion    LLMatrix3::quaternion() const
{
    LLQuaternion    quat;
    F32     tr, s, q[4];
    U32     i, j, k;
    U32     nxt[3] = {1, 2, 0};

    tr = mMatrix[0][0] + mMatrix[1][1] + mMatrix[2][2];

    // check the diagonal
    if (tr > 0.f) 
    {
        s = (F32)sqrt (tr + 1.f);
        quat.mQ[VS] = s / 2.f;
        s = 0.5f / s;
        quat.mQ[VX] = (mMatrix[1][2] - mMatrix[2][1]) * s;
        quat.mQ[VY] = (mMatrix[2][0] - mMatrix[0][2]) * s;
        quat.mQ[VZ] = (mMatrix[0][1] - mMatrix[1][0]) * s;
    } 
    else
    {       
        // diagonal is negative
        i = 0;
        if (mMatrix[1][1] > mMatrix[0][0]) 
            i = 1;
        if (mMatrix[2][2] > mMatrix[i][i]) 
            i = 2;

        j = nxt[i];
        k = nxt[j];


        s = (F32)sqrt ((mMatrix[i][i] - (mMatrix[j][j] + mMatrix[k][k])) + 1.f);

        q[i] = s * 0.5f;

        if (s != 0.f) 
            s = 0.5f / s;

        q[3] = (mMatrix[j][k] - mMatrix[k][j]) * s;
        q[j] = (mMatrix[i][j] + mMatrix[j][i]) * s;
        q[k] = (mMatrix[i][k] + mMatrix[k][i]) * s;

        quat.setQuat(q);
    }
    return quat;
}

normalize()

manual search, walking backward through includes in "llquaternion.cpp"..

#include "linden_common.h"
#include "llmath.h" // for F_PI
#include "llquaternion.h"
//#include "vmath.h"
#include "v3math.h"
#include "v3dmath.h"
#include "v4math.h" ----- Yes
#include "m4math.h" - nope
#include "m3math.h" - nope
#include "llquantize.h" - nope

http://hg.secondlife.com/viewer-development/src/a126abe30997/indra/llmath/v4math.h

Line 84:

        F32 normalize();                // Normalizes and returns the magnitude of LLVector4

Line 458:

inline F32      LLVector4::normalize(void)
{
    F32 mag = (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
    F32 oomag;

    if (mag > FP_MAG_THRESHOLD)
    {
        oomag = 1.f/mag;
        mV[VX] *= oomag;
        mV[VY] *= oomag;
        mV[VZ] *= oomag;
    }
    else
    {
        mV[0] = 0.f;
        mV[1] = 0.f;
        mV[2] = 0.f;
        mag = 0;
    }
    return (mag);
}

No matching function in v4math.cpp so this *.h is the declaration and definition.

Ok a quick C/C++ crash course.
  • To fully explain header files (*.h) would require explaining the architecture of the compiler, which is best to be avoided (as it would rewire more than a paragraph). C is an old language and many of it's features are artifacts of an earlier age's computer limits. One of these quirks is that you can't use a function or type until it has been declared, now you don't need to declare a signature for a function before you define the body of the function, but if you don't you can't have two functions that call each other (function a calls function b which in turn calls function a). Sorting your functions is annoying and time consuming. This is because everything is read top to bottom, until it is encountered the compiler knowns nothing about it, only what has come before. So you are wondering: Why not just do name validation AFTER reading all of the names in, after parsing the files completely? Why would anyone put up with this madness? Answer: It's an artifact left over from when multi-pass processing was impractical. So instead of doing multi-pass processing, they allow you to feed the compiler hints about what is going to come later, to give it a boot strap.
  • Function names can be overloaded, that is to say, you can have multiple functions with the same names but they need to have different parameters. They aren't redefinitions. This way the compiler can determine which version of the function to call based on what you fed it, how many parameters and their types (For added funzies, implicit typecasting can obscure or break things).
I hope this provides enough incite to get you going.
A word of caution, much of this is going to be dependent on how floating point math works and will be unavoidable. To understand this you will want to familiarize yourself with the workings of floats. -- Strife (talk|contribs) 09:50, 9 December 2011 (PST)

Adding Rotations

Why does LSL allow two rotations to be added together? Does it have any use? Omei Qunhua 14:05, 22 January 2014 (PST)

It can give you a halfway rotation, if you're willing to normalize it yourself. You can usually get away without normalizing, if you just want to set. --Cerise Sorbet

Adding two rotations makes no sense whatsoever imho Aleric Inglewood 08:05, 23 March 2014 (PDT)

Yeah, it's pointless without more functionality. I've asked LL in the past for more in the way of rotation functions and operators (like multiplying/dividing them by a scalar, normalization, etc) but they have never bit. -- Strife (talk|contribs) 17:02, 23 March 2014 (PDT)