Difference between revisions of "Rotation/pl"
Line 35: | Line 35: | ||
Przypadek II: | Przypadek II: | ||
Jeżeli wykonamy te same obroty w odwrotnej kolejności, czyli najpierw o 30 stopni wokół Y, rzutka obróci się w dół w płaszczyźnie XZ i nie będzie już leżała na osi X. Układy współrzędnych rzutki i świata nie są już równoległe. Teraz obrót o 45 stopni dookoła osi X spowoduje że tył strzałki pozostanie w miejscu, a czubek będzie nachylony w górę i w prawo pod kątem 30 i 45 stopni względem odpowiednich osi. Pióra rzutki nie są obrócone względem osi długiej rzutki. | |||
W oczywisty sposób efekty końcowe różnią się, chociaż jedyną różnicą jest kolejność przekształceń. | |||
Stałą typu '''rotation''' można utworzyć przez stworzenie {{LSLG|vector|wektora}} z wartościami X, Y, Z składowych obrotu wyrażonych w radianach (tzw. kąt Euler'a) a następnie zmieniając go w typ '''rotation''' używając funkcji {{LSLG|llEuler2Rot}}. Można też stworzyć wartość typu '''rotation''' bezpośrednio: Część rzeczywista to cosinus połowy kąta obrotu, część wektorowa jest znormalizowaną osią obrotu pomnożoną przez sinus połowy kąta obrotu. Do przekształcenia typu '''rotation''' na kąt Eulera typu {{LSLG|vector}} służy funkcja {{LSLG|llRot2Euler}}. | |||
'''NOTE:''' angles in LSL are in radians, not degrees, but you can easily convert by using the built-in constants [[#RAD_TO_DEG|RAD_TO_DEG]] and [[#DEG_TO_RAD|DEG_TO_RAD]]. For a 30 degree '''rotation''' around the X axis you might use: | '''NOTE:''' angles in LSL are in radians, not degrees, but you can easily convert by using the built-in constants [[#RAD_TO_DEG|RAD_TO_DEG]] and [[#DEG_TO_RAD|DEG_TO_RAD]]. For a 30 degree '''rotation''' around the X axis you might use: |
Revision as of 05:11, 17 March 2008
LSL Portal | | | Funkcje | | | Zdarzenia | | | Typy | | | Stałe | | | Potoki | | | Biblioteka Skryptów | | | Tutoriale |
Obroty
LSL'owy typ rotation jest używany do przedstawienia trójwymiarowej orientacji. Orientacja jest reprezentowana przez pojęcie matematyczne nazywane kwaternionem. Można myśleć o kwaternionie jako o czterech liczbach, z których pierwsze trzy reprezentują kierunek w którym obiekt jest skierowany (rozumiany jako wektor 3D), a czwarta z nich to nachylenie (kąt obrotu) w lewo lub w prawo w płaszczyźnie normalnej (prostopadłej) do kierunku skierowania. Podstawową zaletą kwaternionów jest to, ze nie powodują one błędów typu gimbal lock (związanych z równoległością prostych w przestrzeni trójwymiarowej). Jeżeli chcesz zapoznać się ze szczegółami matematycznymi, zobacz kawternion. Rotation Synopsis to lista użytecznych funkcji i zdarzeń (events) odnoszących się do obrotów. Jest też informacja o obracaniu tekstur, tutaj
Inne reprezentacje
Innym sposobem przedstawienia obrotów jest użycie trzech liczb, <X, Y, Z>, które reprezentują kąty (wyrażone w radianach) o które obiekt jest obracany dookoła każdej z osi. Ten rodzaj przedstawienia używany jest w okienku Edycji, i jest generalnie łatwy w wizualizacji. Uwaga, te trzy liczby to typ vector a nie rotation, choć reprezentują tę samą informację. Ten sposób przedstawienia rotacji nazywamy Eulerowskim.
Trzecim rodzajem reprezentacji jest użycie trzech wektorów, wskazujących gdzie jest skierowany przód, góra i lewa strona obiektu. W rzeczywistości potrzebne są tylko dwa z trzech wektorów, ponieważ z dowolnych dwóch da się trzeci wyliczyć.
Z pewnych powodów, takich jak np. możliwość składania obrotów, cztero-liczbowa wersja, rotation, jest lepsza, chociaż na początku może być trudniejsza to wyobrażenia sobie. Na szczęście bardzo rzadko występuje konieczność posługiwania się wewnętrzną reprezentacją obrotów, ponieważ są funkcje konwertujące różne sposoby reprezentacji.
Reguła prawej dłoni
W LSL'u wszystkie obroty są wykonywane zgodnie z regułą prawej dłoni. Ustawiamy dłoń z palcami wyprostowanymi wzdłuż osi OX. Następnie zginamy palce, z wyjątkiem kciuka, wskazując zwrot osi OY. Odwiedziny kciuk wskazuje zwrot osi OZ w układzie prawoskrętnym. Podczas edycji obiektu trzy kolorowe strzałki wskazują dodatni kierunek każdej osi (X: czerwona, Y: zielona, Z: czerwona).
Prawa ręka jeszcze się przyda, do ustalenia kierunku dodatniego obrotu. Palce pięści prawej dłoni, z kciukiem ustawionym w osi obrotu który nas interesuje, wskazują kierunek dodatnigo obrotu wokół tej osi. Obroty wzdłuż osi X, Y i Z są często określane jako "beczka", "nurkowanie" i "zboczenie" (Roll, Pitch, and Yaw), sczególnie dotyczy to pojazdów.
Kierunek obrotów wg Reguły Prawej Dłoni
Składanie obrotów
Do składania obrotów używa się operatorów mnożenia i dzielenia. Nie należy używać dodawania i odejmowania dla obrotów, ponieważ nie dają one spodziewanych rezultatów. Mnożenie obraca w kierunku dodatnim, podczas gdy dzielenie w kierunku ujemnym. Można też odwrócić obrót bezpośrednio, poprzez pomnożenie składowej S przez -1. Np. X.s = -X.s.
W przeciwieństwie do innych typów, np. float, kolejność wykonywania obrotów ma znaczenie.(non-commutative). Spowodowane jest to prostym zjawiskiem - W RL'u kolejność w jakiej wykonuje się obroty również ma znaczenie. Jeżeli weźmiemy rzutkę (strzałkę) z czterema piórami, z początkowym obrotem <0, 0, 0>, której tylna część jest punktem zero, będzie ona leżała wzdłuż osi X, skierowana szpicem w kierunku dodatnim, z piórami wzdłuż osi Z i Y. Osie rzutki i osie świata są równoległe. Obrócimy teraz naszą rzutkę o 45 stopni dookoła osi X i 30 stopnia dookoła osi Y dwa razy, zmieniając kolejność.
Przypadek I: Po obróceniu o 45 stopni dookoła osi X, rzutka dalej leży na tej osi, tylko z piórami pod kątem 45 stopni do osi Z i Y. Obrócenie rzutki o 30 stopni dookoła osi Y przesunie ją w płaszczyźnie XZ, tak by czubek był odchylony o 30 stopni od osi X w dół (wg reguły prawej dłoni, małe wartości obrotu wokół osi Y opuszczają czubek w dół). Rzutka jest teraz skierowana pod kątem 30 stopni w dół, w tej samej płaszczyźnie pionowej, obrócona wokół swojej długiej osi, tak że pióra nie są już skierowane pionowo i poziomo.
Przypadek II:
Jeżeli wykonamy te same obroty w odwrotnej kolejności, czyli najpierw o 30 stopni wokół Y, rzutka obróci się w dół w płaszczyźnie XZ i nie będzie już leżała na osi X. Układy współrzędnych rzutki i świata nie są już równoległe. Teraz obrót o 45 stopni dookoła osi X spowoduje że tył strzałki pozostanie w miejscu, a czubek będzie nachylony w górę i w prawo pod kątem 30 i 45 stopni względem odpowiednich osi. Pióra rzutki nie są obrócone względem osi długiej rzutki.
W oczywisty sposób efekty końcowe różnią się, chociaż jedyną różnicą jest kolejność przekształceń.
Stałą typu rotation można utworzyć przez stworzenie wektora z wartościami X, Y, Z składowych obrotu wyrażonych w radianach (tzw. kąt Euler'a) a następnie zmieniając go w typ rotation używając funkcji llEuler2Rot. Można też stworzyć wartość typu rotation bezpośrednio: Część rzeczywista to cosinus połowy kąta obrotu, część wektorowa jest znormalizowaną osią obrotu pomnożoną przez sinus połowy kąta obrotu. Do przekształcenia typu rotation na kąt Eulera typu vector służy funkcja llRot2Euler.
NOTE: angles in LSL are in radians, not degrees, but you can easily convert by using the built-in constants RAD_TO_DEG and DEG_TO_RAD. For a 30 degree rotation around the X axis you might use:
rotation rot30X | = llEuler2Rot(<30, 0, 0> * DEG_TO_RAD); | // convert the degrees to radians, then convert that vector into a rotation, rot30x |
vector vec30X | = llRot2Euler(rot30X ); | // convert the rotation back to a vector (the values will be in radians) |
Order of rotation for Euler Vectors
From the above discussion, it's clear that when dealing with rotations around more than one axis, the order they are done in is critical. In the Euler discussion above this was kind of glossed over a bit, the individual rotations around the three axis define an overall rotation, but that begs the question: What axis order are the rotations done in? The answer is Z, Y, X in global coordinates. If you are trying to rotate an object around more than one axis at a time using the Euler representation, determine the correct Euler vector using the Z, Y, X rotation order, then use the llEuler2Rot function to get the rotation for use in combining rotations or applying the rotation to the object.
Local vs Global (World) rotations
It is important to distinguish between the rotation relative to the world, and the rotation relative to the local object itself. In the editor, you can switch back and forth from one to the other. In a script, you must convert from one to the other to get the desired behavior.
Local rotations are ones done around the axes embedded in the object itself forward/back, left/right, up/down, irrespective of how the object is rotated in the world. Global rotations are ones done around the world axes, North/South, East/West, Higher/Lower. You can see the difference by rotating a prim, then edit it and change the axes settings between local and global, notice how the colored axes arrows change.
In LSL, the difference between doing a local or global rotation is the order the rotations are evaluated in the statement.
This does a local 30 degree rotation by putting the constant 30 degree rotation to the left of the object's starting rotation (myRot). It is like the first operation in the first example above, just twisting the dart 30 degrees around its own long axis.
rotation localRot = | rot30X * myRot; | // do a local rotation by multiplying a constant rotation by a world rotation |
To do a global rotation, use the same rotation values, but in the opposite order. This is like the second operation in the second example, the dart rotating up and to the right around the world X axis. In this case, the existing rotation (myRot) is rotated 30 degrees around the global X axis.
rotation globalRot | = myRot * rot30X; | // do a global rotation by multiplying a world rotation by a constant rotation |
Another way to think about combining rotations
You may want to think about this local vs global difference by considering that rotations are done in evaluation order, that is left to right except for parenthesized expressions.
In the localRot case, what happened was that starting from <0, 0, 0>, the rot30X was done first, rotating the prim around the world X axis, but since when it's unrotated, the local and global axes are identical it has the effect of doing the rotation around the object's local X axis. Then the second rotation myRot was done which rotated the prim to its original rotation, but now with the additional X axis rotation baked in. What this looks like is that the prim rotated in place, around its own X axis, with the Y and Z rotations unchanged, a local rotation.
In the globalRot case, again starting from <0, 0, 0>, first the object is rotated to its original rotation (myRot), but now the object's axes and the world's axes are no longer aligned! So, the second rotation rot30x does exactly what it did in the local case, rotates the object 30 degrees around the world X axis, but the effect is to rotate the object through a cone around the world X axis since the object's X axis and the world's X axis aren't the same this time. What this looks like is that the prim pivoted 30 degrees around the world X axis, hence a global rotation.
Division of rotations has the effect of doing the rotation in the opposite direction, multiplying by a 330 degree rotation is the same as dividing by a 30 degree rotation.
Using Rotations
You can access the individual components of a rotation R by R.x, R.y, R.z, & R.s (not R.w). The scalar part R.s is the cosine of half the angle of rotation. The vector part (R.x,R.y,R.z) is the product of the normalized axis of rotation and the sine of half the angle of rotation. You can generate an inverse rotation by negating the x,y,z members (or by making the s value negative). As an aside, you can also use a rotation just as a repository of float values, each rotation stores four of them and a list consisting of rotation is more efficient than a list consisting of floats, but there is overhead in unpacking them.
rotation rot30X | = llEuler2Rot(<30, 0, 0> * DEG_TO_RAD ); | // Create a rotation constant |
rotation rotCopy | = rot30X; | // Just copy it into rotCopy, it copies all 4 float components |
float X | = rotCopy.x; | // Get out the individual components of the rotation |
float Y | = rotCopy.y; | |
float Z | = rotCopy.z; | |
float S | = rotCopy.s; | |
rotation anotherCopy | = <X, Y, Z, S>; | // Make another rotation out of the components |
There is a built in constant for a zero rotation ZERO_ROTATION which you can use directly or, if you need to invert a rotation R, divide ZERO_ROTATION by R. As a reminder from above, this works by first rotating to the zero position, then because it is a divide, rotating in the opposite sense to the original rotation, thereby doing the inverse rotation.
rotation rot330X | = <-rot30X.x, -rot30X.y, -rot30X.z, rot30X.s>; | // invert a rotation - NOTE the s component isn't negated |
rotation another330X | = ZERO_ROTATION / rot30X; | // invert a rotation by division, same result as rot330X |
rotation yetanother330X | = <rot30X.x, rot30X.y, rot30X.z, -rot30X.s>; | // not literally the same but works the same. |
Single or Root Prims vs Linked Prims vs Attachments
The reason for talking about single or linked prim rotations is that for things like doors on vehicles, the desired motion is to move the door relative to the vehicle, no matter what the rotation of the overall vehicle is. While possible to do this with global rotations, it would quickly grow tedious. There are generally three coordinate systems a prim can be in: all alone, part of a linkset, or part of an attachment. When a prim is alone, i.e., not part of a linkset, it acts like a root prim; when it is part of an attachment, it acts differently and is a bit broken.
Function | Ground (rez'ed) Prims | Attached Prims | ||
---|---|---|---|---|
Root | Children | Root | Children | |
llGetRot llGPP:PRIM_ROTATION llGetObjectDetails |
global rotation of prim | global rotation of prim | global rotation of avatar | global rotation of avatar * global rotation of prim (Not Useful) |
llGetLocalRot llGPP:PRIM_ROT_LOCAL |
global rotation of prim | rotation of prim relative to root prim | rotation of attachment relative to attach point | rotation of prim relative to root prim |
llGetRootRotation | global rotation of prim | global rotation of root prim | global rotation of avatar | global rotation of avatar |
llSetRot* llSPP:PRIM_ROTATION* |
set global rotation | complicated, see llSetRot | set rotation relative to attach point | set rotation to root attachment rotation * new_rot. |
llSetLocalRot* llSPP:PRIM_ROT_LOCAL* |
set global rotation | set rotation of prim relative to root prim | set rotation relative to attach point | set rotation of prim relative to root prim |
llTargetOmega† ll[GS]PP:PRIM_OMEGA |
spin linkset around prim's location | spin prim around its location | spin linkset around attach point | spin prim around its location |
* | Physical objects which are not children in a linkset will not respond to setting rotations. |
† | For non-Physical objects llTargetOmega is executed on the client side, providing a simple low lag method to do smooth continuous rotation. |
Rotating Vectors
In LSL, rotating a vector is very useful if you want to move an object in an arc or circle when the center of rotation isn't the center of the object.
This sounds very complex, but there is much less here than meets the eye. Remember from the above discussion of rotating the dart, and replace the physical dart with a vector whose origin is the tail of the dart, and whose components in X, Y, and Z describe the position of the tip of the dart. Rotating the dart around its tail moves the tip of the dart through and arc whose center of rotation is the tail of the dart. In exactly the same way, rotating a vector which represents an offset from the center of a prim rotates the prim through the same arc. What this looks like is the object rotates around a position offset by the vector from the center of the prim.
rotation rot6X | = llEuler2Rot(<30, 0, 0> * DEG_TO_RAD ); | // create a rotation constant, 30 degrees around the local X axis |
vector offset | = <0, 1, 0>; | // create an offset one meter in the global positive Y direction |
vector rotatedOffset | = offset * rot6X; | // rotate the offset to get the motion caused by the rotations |
vector newPos | = llGetPos() + (offset - rotatedOffset) * llGetRot(); | // move the prim position by the rotated offset amount |
rotation newRot | = rot6X * llGetRot(); | // change rot to continue facing offset point |
<lsl> //-- same as: llSetPrimitiveParams( [PRIM_POSITION, llGetPos() + (gPosOffset - gPosOffset * gRotArc) * llGetRot(),
PRIM_ROTATION, gRotArc * llGetRot() );
</lsl> Nota Bene: Doing this is a move, so don't forget about issues of moving a prim off world, below ground, more than 10 meters etc.
To get a point relative to the objects current facing (such as used in rezzors)
rotation rotFacing | = llGetRot(); | // get the objects current rotation |
vector offset | = <0, 0, 1>; | // create an offset one meter in the global positive Z direction |
vector rotatedOffset | = offset * rotFacing; | // rotate the offset to be relative to object rotation |
vector correctOffset | = llGetPos() + rotatedOffset; | // get the global position of the local offset |
<lsl> //-- same as: vector correctOffset = llGetPos() + offset * llGetRot(); </lsl>
Constants
ZERO_ROTATION
ZERO_ROTATION = <0.0, 0.0, 0.0, 1.0>;
A rotation constant representing a Euler angle of <0.0, 0.0, 0.0>.
DEG_TO_RAD
DEG_TO_RAD = 0.01745329238f;
A float constant that when multiplied by an angle in degrees gives the angle in radians.
RAD_TO_DEG
RAD_TO_DEG = 57.29578f;
A float constant when multiplied by an angle in radians gives the angle in degrees.